Many instructions work on the data stack or the type stack. The effect that an instruction has on the stack is shown in a before and after view of what is on the stack. In each view, the top part of the stack is shown, from top to bottom. There can be more of the stack below the part that is shown. What is shown in the after view replaces what is shown in the before view. For example, the MINUS instruction has the following stack diagram.
Data stack | |
---|---|
before | after |
Y** : (any numeric species) | (X - Y)** : (a numeric species) |
X** : (any numeric species) |
This instruction pops two numbers (first Y then X) and then pushes X - Y. Notice that X and Y were pushed in the opposite order from the order in which they are popped. First X was pushed and then Y was pushed. So you think of X as the first parameter of MINUS, and Y as the second parameter.
Items on the stack can be promises. The MINUS instruction needs to evaluate any promises so that it can perform the subtraction. The * indicates this evaluation. See forcing evaluation for the meaning of evaluation marks.
A label declaration provides a way to refer to the thing declared in the declaration that immediately follows the label. Each label declaration gives a number that indicates the label number. Labels created in label declarations are called global labels.
When a global label is written in an assembler program, you have the option of preceding the label number by a > sign. For example, you can write
@LABEL >20
The program uses names for items by declaring them in an declarations just after a label instruction, and thereafter referring to them by the label. For example,
@LABEL 1 @ID kangarooindicates that label 1 refers to name kangaroo. An @ID declaration is only used to define an identifier that will be used to refer to a data item.
A @NAME declaration is similar to a @ID declaration, but is used to create a name that refers to something other than a data item.
A string declaration produces a string constant, which can contain any characters, as well as the escape sequences \n (newline), \t (tab), \r (carriage return), \\ (backslash), \" (quote) and \{n} (character code n) where n is a decimal integer. The string is written inside double-quote marks. It can begin either on the same line as @STRING or on the next line, and can continue for any number of lines. (A line break inside the string is part of the string.)
The species of the thing pushed is List(Char) (a list of characters).
Typically a @STRING declaration is preceded by a label, and the string is referred to by that label.
Examples:
@LABEL 1 @STRING "you can have short strings" @LABEL 2 @STRING "or you can have longer strings that span several lines and have special things like \t and \n or \{3}." @LABEL 3 @STRING "another string constant"
Define an integer constant. The integer is written in decimal, possibly with a sign.
The species of the constant is Integer.
Typically, an @INT declaration is preceded by a label, and the integer is referred to by that label.
Examples:
@LABEL 41 @INT 456 @LABEL 42 @INT -123456789123456789
Define a real constant. The number written in decimal, possibly with a sign. There can be a decimal point, with at least one digit on each side of the decimal point. There number can end with Eexp where exp is a (signed) decimal exponent of 10. There cannot be any spaces in the number. For example,
@LABEL 19 @REAL 3.67E-3defines the real constant 0.00367
The species of the constant is Real.
Typically a @REAL declaration is preceded by a label, and the number is referred to by that label.
Define a rational constant. The number n is written in the same format as for a @REAL declaration.
The species of the constant is Rational.
Typically a @RAT declaration is preceded by a label, and the number is referred to by that label.
Define a list constant. The number n indicates how many members the list has. After the @LIST declaration must be exactly n declarations of constants that are the members of the list. The constants can be define by @INT, @REAL, @RAT, @STRING and @LIST declarations. But all of the constants must be of the same species.
The species of the constant is List(T) where T is the species of each of the constants in the list. (When the constants are defined using INT, the species can be either List(Natural) or List(Integer).)
Typically a real declaration is preceded by a label, and the number is referred to by that label.
Example:
@LABEL 20 @LIST 4 @INT 18 @INT 22 @INT -5 @INT 17creates the list constant [18, 22, -5, 17], whose species is List(Integer).
Evaluate an expression. The general form is as follows.
Form of @EXECUTE | |
---|---|
@EXECUTE n | Begin the global definition fetch. The number n tells an upper limit on the number of slots that are required in the static environment. That is, it tells how many definitions will be fetched. See specifying the size of the environment. |
instructions | These instructions fetch definitions from the global tables that this code wants to use. See building the static environment. |
BEGIN-EXECUTABLE i | Begin the executable part. The number i tells an upper limit on the number of slots that are required in the local environment during the evaluation. See specifying the size of the environment. |
instructions | These instructions are performed |
END | One byte |
Evaluate an expression. This is exactly the same as an @EXECUTE, but it indicates that the actions performed are probably of no interest to a debugger.
Create a definition in the global tables. The general form is as follows.
Form of @DEFINE | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
@DEFINE L mode | This definition defines the identifier that is defined (using an @ID declaration) at label L. Mode is a string of zero or more characters, without intervening spaces, indicating how the definition is entered into the table. The possible characters are as follows. (The case does not matter. You can use either lower case or upper case letter here.)
|
||||||||||
type instructions | This sequence of instructions is responsible for putting the polymorphic type of this definition on the top of the type stack. | ||||||||||
BEGIN-GLOBALS n | Begin a sequence of instructions that fetch definitions from the global definition table. The number n tells an upper limit on the number of slots that are required in the static environment. See specifying the size of the environment. | ||||||||||
instructions | These instructions fetch definitions from the global tables that this code wants to use. See building the static environment. | ||||||||||
BEGIN-EXECUTABLE i | Begin the executable part. The number i tells an upper limit on the number of slots that are required in the local environment during the evaluation. See specifying the size of the environment. | ||||||||||
instructions | These instructions are performed to compute the item that this definition names. They leave the value on the top of the data stack when they are done. | ||||||||||
END | One byte |
Begin an extension. All declarations that create new species, parameterized species, genera, or parameterized genera, or that indicate relationships among those things, must occur between @BEGIN-EXTENSION and @END-EXTENSION.
A species, genus, etc. cannot be used until the extension where it is created has ended.
This declaration has no parameters.
End an extension.
Define a new species called name. Typically, this declaration is preceded by a label, so that the species can be referred to by that label.
Example:
@BEGIN-EXTENSION ... @LABEL 5 @NEW-SPECIES Widget ... @END-EXTENSION
Refer to a species that is defined in another package, and that has already been defined via an import, or to a species that is built into the abstract machine. This declaration does not create a new species, but provides a way of referring to one that has already been defined. This declaration is typically preceded by a label.
Example:
@BEGIN-EXTENSION ... @LABEL 5 @IMPORTED-SPECIES Widget ... @END-EXTENSION
Create a new parameterized species called name. Opts tells the characteristics of the family. It must be two letters long, and must contain:
Example:
@BEGIN-EXTENSION ... @LABEL 5 @NEW-FAMILY vc Queue @NEW-FAMILY IN Castle ... @END-EXTENSION
This is similar to a @NEW-FAMILY declaration, but refers to a family that was created elsewhere or that is built into the abstract machine. No options are provided; they are only given when the family is created.
Example:
@BEGIN-EXTENSION ... @LABEL 5 @IMPORTED-FAMILY Queue ... @END-EXTENSION
Create a new genus called name. Opt is either the letter g indicating a gregarious genus or s indicating a shy genus. The option letters can be upper or lower case.
@BEGIN-EXTENSION ... @LABEL 5 @NEW-GENUS s MAMMAL @NEW-GENUS G GRINDABLE ... @END-EXTENSION
This is similar to a @NEW-GENUS declaration, but it refers to a genus that has been imported from another package or that is built into the abstract machine.
Example:
@BEGIN-EXTENSION ... @LABEL 5 @IMPORTED-GENUS MAMMAL ... @END-EXTENSION
This declaration creates a new parameterized genus called name. Opts tells characteristics of the new parameterized genus. It must contain one of each of the following letters:
Example:
@BEGIN-EXTENSION ... @LABEL 5 @NEW-COMMUNITY svc COLLECTION ... @END-EXTENSION
Refer to a parameterized genus that has previously been defined in an imported package or that is built into the abstract machine.
Example:
@BEGIN-EXTENSION ... @LABEL 5 @IMPORTED-COMMUNITY COLLECTION ... @END-EXTENSION
Let TA be the species, genus, etc. that is defined at global label A, and let TB be what is defined at global label B. Declare that TA is beneath TB in the hierarchy.
Example:
@BEGIN-EXTENSION ... @LABEL 5 @IMPORTED-GENUS MAMMAL @LABEL 6 @NEW-SPECIES Kangaroo @RELATE 6 5 % A Kangaroo is a kind of MAMMAL. ... @END-EXTENSION
Let TA be what is defined at global label A, etc. Declare that the meet (intersection) of TA and TB is TC.
Example:
@BEGIN-EXTENSION ... @LABEL 5 @IMPORTED-GENUS MAMMAL @LABEL 6 @NEW-GENUS HOPPER @LABEL 7 @NEW-SPECIES Kangaroo @MEET 5 6 7 ... @END-EXTENSION
Let T1 be what is declared at global label A1, etc. Declare that the meet of T1,...,Tn is empty. That is, no species belongs to all of these things.
Example:
@BEGIN-EXTENSION ... @LABEL 12 @IMPORTED-GENUS MAMMAL @LABEL 13 @NEW-GENUS REPTILE @EMPTY-MEET 12 13 ... @END-EXTENSION
Indicate a default for the genus or parameterized genus at global label L. This declaration is followed by a sequence of instructions that build the default species and put it onto the type stack, followed by an END-SPECIES instruction. Alternatively, a @DEFAULT declaration can be followed immediately by END-SPECIES to cause this genus not to have a default.
Example:
@BEGIN-EXTENSION ... @LABEL 12 @IMPORTED-GENUS MAMMAL @LABEL 13 @IMPORTED-SPECIES Kangaroo @DEFAULT 12 SPECIES-ID 13 END-SPECIES ... @END-EXTENSION
Create a new exception or kind of exception called name. Opt is either missing, indicating that this exception is initially not trapped, or is t indicating that this is initially trapped.
An @EXCEPTION declaration can optionally be followed, starting on the next line, by a string constant surrounded by quotes. The string constant is the description of the exception. (See EXCEPTION-STRING.)
After the string (or after the @EXCEPTION declaration if there is no string) is an optional sequence of species-building instructions that puts the content-species of the exception onto the type stack. This sequence of instructions is followed by END-SPECIES. If no species is given, then END-SPECIES should occur immediately.
An @EXCEPTION declaration is used to create a new exception. If you want to use an exception that was created in another package, use an @ID declaration and look up the exception identifier with species ExceptionSpecies.
Create a new initially empty exception set called name. This is normally preceded by a label declaration, and the exception set is referred to by that label.
Create a new exception set called name that only contains exception or class of exceptions mem. This is normally preceded by a label declaration, and the exception is referred to by that label.
Refer to an exception set defined in another package or that is built into the abstract machine. This exception set must have already been imported or defined. This is normally preceded by a label declaration, and the exception is referred to by that label.
Add the exception or class of exceptions declared at label A to the exception set declared at label B. The exception set being extended must have been created using @NEW-EXCEPTION-SET, not using @NEW-SIMPLE-EXCEPTION-SET.
Read the interface part of package path, given as a full path name of the file.
Example:
@IMPORT /home/myself/mypackage
This declaration separates the interface part of a package from the implementation part. An import of a package will not read beyond this. This also indicates a possible package name change, if the package has two names.
Push a standard species or parameterized species onto the type stack. The possible values of name are
Example:
STD-SPECIES Boolean
Push a standard species or parameterized species onto the type stack. The possible values of name are
Example:
STD-SECONDARY-SPECIES ANY
Push the species or parameterized species defined at global label L onto the type stack.
Example:
SPECIES-ID 4
Push a variable that ranges over genus or parameterized genus name. The possible values of name are
Example:
STD-SPECIES-VAR ANY
Similar to STD-SPECIES-VAR, but push a variable that can only have a primary species as its value.
Similar to STD-SPECIES-VAR, but push a variable that can only have a secondary species as its value.
Push a variable that ranges over the genus or parameterized genus labeled by global label L.
Example:
SPECIES-VAR 40
Similar to SPECIES-VAR, but push a variable that can only have a primary species as its value.
Similar to SPECIES-VAR, but push a variable that can only have a secondary species as its value.
Pop B then A from the type stack, then push A -> B. That is, modify the type stack as follows.
Type stack | |
---|---|
before | after |
B | A -> B |
A |
Pop B then A from the type stack, then push (A, B). That is, modify the type stack as follows.
Type stack | |
---|---|
before | after |
B | (A, B) |
A |
Pop A from the type stack, then push List(A). That is, modify the type stack as follows.
Type stack | |
---|---|
before | after |
A | List(A) |
Pop A from the type stack, then push Box(A). That is, modify the type stack as follows.
Type stack | |
---|---|
before | after |
A | Box(A) |
Pop F then A from the type stack, then push F(A), where F is a parameterized species or variable that stands for a parameterized species. That is, modify the type stack as follows.
Type stack | |
---|---|
before | after |
F | F(A) |
A |
Mark the variable on top of the type stack as preferring a secondary default. Do not modify the type stack itself. If the top of the type stack is not a variable, do nothing.
Modify the type stack in one of the following ways.
Type stack | |
---|---|
before | after |
(A, B) | B |
A |
Type stack | |
---|---|
before | after |
A -> B | B |
A |
Type stack | |
---|---|
before | after |
F(A) | F |
A |
If the top of the type stack does not have any of these forms then it is an error.
Modify the type stack in one of the following ways.
Type stack | |
---|---|
before | after |
(A, B) | A |
Type stack | |
---|---|
before | after |
A -> B | A |
Type stack | |
---|---|
before | after |
F(A) | A |
Modify the type stack in one of the following ways.
Type stack | |
---|---|
before | after |
(A, B) | B |
Type stack | |
---|---|
before | after |
A -> B | B |
Type stack | |
---|---|
before | after |
F(A) | F |
Pop the type stack. That is, modify the type stack in the following way.
Type stack | |
---|---|
before | after |
A |
Swap the top two things on the type stack. That is, modify the type stack in the following way.
Type stack | |
---|---|
before | after |
A | B |
B | A |
Remove all species from the species store and make the next index to be filled be 0.
Pop the type stack and store the popped thing at the next available index in the species store. Add 1 to the next available index. Successive STORE-SPECIES instructions store at indices 0, 1, 2, ...
Type stack | |
---|---|
before | after |
A |
Store what is on top of the type stack at the next available index in the species store. Do not modify the type stack. Add 1 to the next available index.
Type stack | |
---|---|
before | after |
A | A |
Fetch what was stored at index n in the type store and push it onto the type stack.
Type stack | |
---|---|
before | after |
A |
Fetch what was stored at index n in the current function's static environment and push it onto the type stack.
Type stack | |
---|---|
before | after |
A |
Pop the top two species from the type stack and unify them. Bindings of variables are put into the current thread's species variable binding table. If unification fails, then fail with exception speciesX.
Type stack | |
---|---|
before | after |
B | |
A |
Pop B then A from the type stack. Do not push anything back. That is, modify the type stack as follows.
Type stack | |
---|---|
before | after |
B | |
A |
Add a constraint A >= B. That is, constrain the values of variables in A and B so that this must be true.
Replace the type stack top by a copy of itself, replacing each bound species variable by the species to which it is bound. Bindings are found in the current thread's binding table.
Type stack | |
---|---|
before | after |
A | |
A' |
Push the current thread's species binding table onto its binding stack so that it can be restored later using POP-SPECIES-BINDINGS.
Pop the current thread's species binding table stack and put the popped table into its species binding table.
Replace the top of the current thread's species binding table stack by the current species variable binding table. This will ensure that a subsequent POP-SPECIES-BINDING instruction will leave the table the way it is now.
Pop the type stack and push the popped species onto the data stack.
|
|
Pop the name of a species or parameterized species (a string) from the data stack, and push the corresponding species or parameterized species in its place. That is, modify the data stack as follows.
Modify the data stack as follows.
Modify the data stack as follows.
Modify the data stack as follows.
Get the domain and codomain of a function species. Modify the data stack as follows.
If the species on the data stack is not a function species then fail with exception testX.
Get the components of a cartesian product species. Modify the data stack as follows.
If the species on the data stack is not a cartesian product then fail with exception testX.
Get the family and parameter from a species that is constructed by a family. Modify the data stack as follows.
If the species on the data stack is not constructed by a family, fail with exception testX.
Modify the data stack as follows.
Compare species A and B. C is 0 if A = B, 1 if A > B and 2 if A < B.
Modify the data stack as follows.
X can be of any secondary species, and so must be tagged by a species. Get the species tag T of S. The result R is true if T is <= A, and is false otherwise.
Modify the data stack as follows.
The result R is true if A is a primary species or family and false otherwise.
Get a string that describes a species or family.
This instruction should only be used in the static environment building part of a @DEFINE or @EXECUTE declaration.
Pop a species T from the type stack. Get the definition of the name (say, name) defined at label L (by an @ID declaration) of species T from the global definition table. Store the value obtained at the next available slot in the static environment.
If there is no such definition, this instruction will continue, but an attempt to get this identifier and evaluate it will fail with exception noDefX(name:T).
The species T of this identifier is also stored in the static environment. An attempt to get a species at the offset of this global identifier will get species T.
This instruction should only be used in the static environment building part of a @DEFINE or @EXECUTE declaration.
Pop a species T from the type stack and store it at the next available slot in the static environment.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
A | A |
A |
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
A |
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
A | B |
B | A |
Push (). Modify the data stack as follows.
Data stack | |
---|---|
before | after |
()* |
Push 0. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
0** : Integer |
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
n** : Integer |
Here n must be an integer from 0 to 255.
Push the constant defined at global label L onto the data stack. That label must occur prior to this instruction in the program. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
C* |
where C is the constant defined at global label L.
Test for 0. Modify the data stack as follows.
The result R is true if N is 0 and is false otherwise.
Test for an odd number. Modify the data stack as follows.
Modify the data stack as follows.
The result R = -1 if N < 0, R = 0 if N = 0 and R = 1 if N > 0.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
Y** : (any numeric species) | (X + Y)** : (a numeric species) |
X** : (any numeric species) |
Compute the sum of two numbers. The result species depends on the parameter species, and is determined by using COMPATIFY-NUMBERS.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
Y** : (any numeric species) | (X - Y)** : (a numeric species) |
X** : (any numeric species) |
Compute the difference between two numbers. The result species depends on the parameter species, and is determined by using COMPATIFY-NUMBERS.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
Y** : (any numeric species) | ( |X - Y| )** : (a numeric species) |
X** : (any numeric species) |
Compute the absolute difference between two numbers. The result species depends on the parameter speciess, and is determined by using COMPATIFY-NUMBERS.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
Y** : (any numeric species) | (X * Y)** : (a numeric species) |
X** : (any numeric species) |
Compute the product two numbers. The result species depends on the parameter speciess, and is determined by using COMPATIFY-NUMBERS.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
Y** : (any numeric species) | (X / Y)** : (a numeric species) |
X** : (any numeric species) |
Compute the quotient of two numbers. The result species depends on the parameter speciess, and is determined by using COMPATIFY-NUMBERS.
Modify the data stack as follows.
Compute the greatest common divisor of two nonnegative integers. If both numbers are 0, then this instruction fails with exception domainX("gcd(0,0)"). It is an error if either of X or Y is negative.
Modify the data stack as follows.
Compute a number to an integer power. The result species is the same as the species of X. If Y < 0, then the species of X must be either Rational or Real. If both X and Y are 0 then this instruction fails with exception domainX("0^0").
Modify the data stack as follows.
Modify the data stack as follows.
Modify the data stack as follows.
Modify the data stack as follows.
Modify the data stack as follows.
Compute the quotient Q and remainder R when X is divided by Y. It is required that Y > 0. Q and R satisfy the following requirements.
If Y = 0 this instruction fails with exception domainX("divide by zero"). If Y < 0 then this instruction fails with exception domainX("integer divide by negative").
Modify the data stack as follows.
Let b be the internal numeric base. This instruction choose m and e so that m*be is an approximation of 1/X. Integer m has about the same number of base b digits as does X.
X must be a positive integer. If X is 0 this instruction fails with exception domainX("divide by zero"). If X < 0 this instruction fails with exception domainX("integer divide by negative").
Modify the data stack as follows.
The square root is computed to standard precision, even if X has high precision.
Modify the data stack as follows.
exp(X) is eX. It is computed to standard precision, even if X has high precision.
Modify the data stack as follows.
ln(X) is the natural logarithm of X. It is computed to standard precision, even if X has high precision.
Modify the data stack as follows.
The result is computed to standard precision, even if X has high precision.
Modify the data stack as follows.
The result is computed to standard precision, even if X has high precision.
Modify the data stack as follows.
The result is the largest integer that is not greater than X.
Modify the data stack as follows.
The result is the smallest integer that is not less than X.
Modify the data stack as follows.
X is converted to Natural. The conversion is only possible if X is some representation of a nonnegative integer. This instruction fails with exception conversionX if the conversion is not possible.
Modify the data stack as follows.
X is converted to Integer. The conversion is only possible if X is some representation of an integer. This instruction fails with exception conversionX if the conversion is not possible.
Modify the data stack as follows.
X is converted to Rational.
Modify the data stack as follows.
X is converted to Real.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
Y** : (any numeric species) | X'** : (same species as Y) |
X** : (any numeric species) |
X' is the result of converting X to the same species as Y. If the conversion is not possible, this instruction fails with exception conversionX.
Modify the data stack as follows.
Convert a string S of decimal digits to a natural number. The string can contain leading and trailing white space, defined as a sequence of spaces, tabs, linefeeds and carriage-returns. This instruction fails with exception conversionX if S is not a string of decimal digits, optionally preceded or followed by white space.
Modify the data stack as follows.
Convert a string S to an integer. S must be in decimal notation, optionally preceded by a + or - sign. The string can contain leading and trailing white space, defined as a sequence of spaces, tabs, linefeeds and carriage-returns. This instruction fails with exception conversionX if S is not an optionally signed string of decimal digits, optionally preceded or followed by white space.
Modify the data stack as follows.
Convert a string S to a rational number. The string consists of
This instruction fails with exception conversionX if S is not of the correct form.
Modify the data stack as follows.
Convert a string S a real number. The form is identical to the form for STRING-TO-RATIONAL.
This instruction fails with exception conversionX if S is not of the correct form.
Modify the data stack as follows.
Convert a string of hexadecimal digits to a natural number. The digits can include the first six letters of the alphabet, either upper or lower case. This instruction fails with exception conversionX if S is not of the correct form.
Modify the data stack as follows.
Convert a natural number to a string of hexadecimal digits.
Modify the data stack as follows.
N is an approximation of the number of internal digits needed to represent X, or the number of internal digits in the mantissa of X for a real number.
Modify the data stack as follows.
N is an approximation of the number of decimal digits needed to represent X, or the number of decimal digits in the mantissa of X for a real number.
Modify the data stack as follows.
This instruction computes integers m and e such that X = m*be, where b is the internal base.
Modify the data stack as follows.
This instruction is the inverse of PULL-APART-REAL.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
P** : Natural |
The result P is the current content of box precision for the current thread, but converted to internal digits rather than in decimal digits.
Modify the data stack as follows.
The result S is a string that describes X. The species that X can have are as follows.
What TO-STRING can convert | |
---|---|
Integer | S is a decimal representation of X. |
Rational | S is a string of the form "n/d" where n is an integer and d is a positive integer. For example, if X is 1/3 then S is "1/3". If the denominator is 1 then a rational number is shown as an integer. |
Real | S is a string that describes real number X. The format is chosen by the system. If you need a particular format, use TO-STRING-FORMATTED. |
A function | S is a string that describes function X. It has the form "(function name:species)" where name is the function name and species is this function's species. |
Modify the data stack as follows.
The result S is a string that describes X, using F as formatting information. The species that X and F can have are as follows.
Formats | |
---|---|
X : Integer | The format F must be a natural number. S is a decimal representation of X in at least F spaces, padded on the left with blanks. Padding will be done only if X has fewer than F digits, in which case S will have exactly F characters. |
X : Rational | F can be either a natural number or a pair of natural numbers. A natural number N has the same effect as (N, 0). Suppose F is (a,b). Then S is a string of the form "n/d" where d is a positive integer formatted with format parameter b, and the entire string is padded up to a characters by padding on the left with blanks if necessary. For example, if X is 1/3 and F is (5,2) then S is " 1/ 3". |
X : Real | The format parameter F can be a natural number, a pair of natural numbers or a triple (n, (d, e)). If F is 0 then the number is formatted by the interpreter in a way that it chooses. A natural number n > 0 is equivalent to (n, (0, 0)). A pair (n,d) is equivalent to (n, (d, 0)). Suppose that F is (n, d, e). If e is 0 then X is formatted in fixed point style, with a total of n or more spaces (padded with blanks on the left if necessary) and with d digits to the right of the decimal point. If d is 0, then no decimal point is shown. If e > 0 then X is formatted in scientific notation, with an E followed by a signed exponent of 10. Number e tells the number of digits, excluding the sign, to use for the exponent. |
Modify the data stack as follows.
The result R is 0 if X = Y, 1 if X > Y, and 2 if X < Y.
Modify the data stack in one of the following ways
Data stack | |
---|---|
before | after |
Y** : (any numeric species) | R** : Boolean |
X** : (any numeric species) |
Data stack | |
---|---|
before | after |
Y* : Box(B) | S** : Boolean |
X* : Box(A) |
Data stack | |
---|---|
before | after |
Y*** : List(numeric species) | W** : Boolean |
X*** : List(numeric species) |
The result R is true if X = Y, and false otherwise. Result S is true if X and Y are the same box, and false otherwise. Result W is true if X and Y are the same string or list of numbers.
NE is similar to EQ, but it pushes false when EQ would push true, and it pushes true when EQ would push false.
Modify the data stack as follows.
The result R is true if X < Y, and false otherwise.
Modify the data stack as follows.
The result R is true if X <= Y, and false otherwise.
Modify the data stack as follows.
The result R is true if X > Y, and false otherwise.
Modify the data stack as follows.
The result R is true if X >= Y, and false otherwise.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
false* |
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
true* |
Modify the data stack as follows.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
[]* |
Modify the data stack as follows.
The result R is true if X is [], and false if X is not [].
Modify the data stack as follows.
This instruction fails with exception testX if X is []. Otherwise, it does nothing.
Modify the data stack as follows.
This instruction fails with exception testX if X is []. If X is an unbound unprotected unknown, then it binds X to a list having an unknown head and an unknown tail, using two new unbound, unprotected unknowns of the same flavor as X. (If X is shared, the two new unknowns are also shared. If X is nonshared, the two new unknowns are also nonshared.) If X is already known to be a nonempty list, this instruction does nothing.
Modify the data stack as follows.
This instruction fails with exception testX if X is not []. If X is an unbound unprotected unknown, then it binds X to []. If X is [], this instruction does nothing.
Modify the data stack in one of the following ways
The ordered pair (X, Y) and the list X::Y (whose head is X and whose tail is Y) have the same representation, so this instruction builds both.
Perform n PAIR instructions.
Modify the data stack in one of the following ways.
This instruction fails with exception emptyListX if the stack contains [].
Modify the data stack in one of the following ways.
Data stack | |
---|---|
before | after |
(X, Y) | X |
Data stack | |
---|---|
before | after |
X :: Y | X |
This instruction is the same as HEAD, except that the value pushed is lazy. The intial top of the stack is not evaluated immediately. If there is a failure, then the failure will occur when this value is forced, not immediately.
Modify the data stack in one of the following ways.
This instruction fails with exception emptyListX if the stack contains [].
Modify the data stack in one of the following ways.
Data stack | |
---|---|
before | after |
(X, Y) | Y |
Data stack | |
---|---|
before | after |
X :: Y | Y |
This instruction is the same as TAIL, except that the value pushed is lazy. The intial top of the stack is not evaluated immediately. If there is a failure, then the failure will occur when this value is forced, not immediately.
Same as HEAD, but if the top of the stack is initially an unbound unprotected unknown, then bind it to a pair of unbound unprotected unknowns first. Preserve the unknown flavor.
Same as LAZY-HEAD, but when the result is forced, if the value whose head is being taken is an unbound unprotected unknown, then bind it to a pair of unbound unprotected unknowns first. Preserve the unknown flavor.
Same as TAIL, but if the top of the stack is initially an unbound unprotected unknown, then bind it to a pair of unbound unprotected unknowns first. Preserve the unknown flavor.
Same as LAZY-TAIL, but when the result is forced, if the value whose tail is being taken is an unbound unprotected unknown, then bind it to a pair of unbound unprotected unknowns first. Preserve the unknown flavor.
Perform n ACTIVE-TAIL instructions.
Modify the data stack in one of the following ways.
This instruction fails with exception emptyListX if the stack contains [].
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X**** : List(T) | length(X) : Natural |
Modify the data stack as follows.
The result R is the length of list L provided that length is at most N. If the length of L is larger than N, then R will be some number larger than N.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X**** : List(T) | reverse(X) : List(T) |
reverse(X) is the reversal of list X.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
Y : List(T) | X + Y : List(T) |
X : List(T) |
X + Y is the concatenation of list X followed by list Y.
Modify the data stack as follows.
Here L[N] indicate the N-th member of L, numbering from 0. So L[0] is the head of L, L[1] the second member of L, etc.
This instruction fails with exception subscriptX if there is no N-th member of L.
Modify the data stack as follows.
The result list R is obtained from list L by (1) removing the first M members, and (2) taking a prefix of length N of what is left. For example, if L is [1,2,3,4,5,6,7], M is 2 and N is 3 then R is [3,4,5].
If L has fewer than M members then R is an empty list. If L has M or more members, but fewer than M + N, then R is the result of removing the first M members of L.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
L**** : List(T) | (A, B) : (List(T), List(T)) |
If L is [], then A = [] and B = []. Otherwise, A and B are chosen so that A is not [], and L = A + B (the concenation A followed by B).
This instruction is intended to extract an "efficient" prefix so that a function can process a list in chunks more efficiently than processing it one member at a time. This function will avoid evaluating promises when it can.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
(P,W,S)** : (Char, Integer, Boolean) | (i,r,q) : (Natural, List(Char), Char) |
S**** : List(Char) |
Integer W is treated as a bit vector that describes a set of characters. The n-th bit is 1 if character n is in the set, where bits are numbered from 0 starting at the low-order end.
If S is true, then i, r and q are chosen so that
Modify the data stack as follows.
If M > N then [] is pushed.
Modify the data stack as follows.
If M < N then [] is pushed.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N** : Natural | N : Natural |
L |
Create a shell L suitable for extending to a fully defined list of length N using ADD-LIST-MEM. This list must be finished using ADD-LIST-MEM N times before it is ready to use. The list that is created uses random-access (packed) representation.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
V | N-1 |
N** : Natural | L |
L |
Add value V to list L at index N-1. You build a list backwards using these instructions. For example, to create list [10,20,30,40], you could use the following sequence of instructions.
PUSH-INT 4 MAKE-START-LIST PUSH-INT 40 ADD-LIST-MEM PUSH-INT 30 ADD-LIST-MEM PUSH-INT 20 ADD-LIST-MEM PUSH-INT 10 ADD-LIST-MEM POP %% List [10,20,30,40] is now on the data stack.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
F : T -> S | (map F L) : List(S) |
L : List(T) |
If L is [a,b,c,d] then (map F L) is the list [F(a), F(b), F(c), F(d)]. The list that is created is dynamically mapped, and does not memoize its result. Each time a member of the result list is needed, the corresponding member of L is found, and F is applied to it.
Get the species List(T) -> S stored at index k in the static environment. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
L* : List(T) | R |
If L is [a,b,c,d] then the result list R is [(a:T), (b:T), (c:T), (d:T)] where each member is tagged with species A. Species A must be a primary species; otherwise this instruction fails with exception speciesX.
Exception. If a member a of list L is already tagged by a species then no new tag is added to a. Instead, a will be placed unchanged into the result list.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
L**** : List(T) | L : List(T) |
From a semantic perspective, this instruction has no effect. But it might perform a change or representation. The list on the stack after a PACK will be in random-access, or packed, representation.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
L**** : List(Char) | L : List(Char) |
From a semantic perspective, this instruction has no effect. But it might perform a change or representation. The string is put into a hash table, and the new L refers to that string. This has two effects on performance. One is that, if you have several copies of the same string, the string will only be stored once. The other is that equality tests are efficient when both strings have been interned. (An equality test between interned strings is done by comparing memory addresses.)
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
name**** : List(Char) | sym : Symbol |
Convert a string name into a symbol. This is the same as INTERN-STRING. It puts the string into a hash table.
Take the bitwise and of two integers. An integer is thought of as standing for its infinite two's comlement representation. so 0 is an infinite vector of 0's, and -1 is an infinite vector of 1's.
Take the bitwise or of two integers. An integer is thought of as standing for its infinite two's comlement representation. so 0 is an infinite vector of 0's, and -1 is an infinite vector of 1's.
Take the bitwise exclusive-or of two integers. An integer is thought of as standing for its infinite two's comlement representation. so 0 is an infinite vector of 0's, and -1 is an infinite vector of 1's.
Shift an integer to the left, pulling in 0 bits on the right end.
R is the result of shifting X to the left N bits, pulling in 0 bits on the right. So if Xis 3 and N is 2 then R is 12. X is thought of as standing for its infinite two's comlement representation. so 0 is an infinite vector of 0's, and -1 is an infinite vector of 1's.
Shift an integer to the right.
R is the result of shifting X to the right N bits. X is thought of as standing for its infinite two's comlement representation. so 0 is an infinite vector of 0's, and -1 is an infinite vector of 1's.
Shift an integer to the left by a given number of internal digits.
R is the result of shifting X to the left N internal digits, pulling in 0 digits on the right.
Shift an integer to the right by a given number of internal digits.
R is the result of shifting X to the right N internal digits.
R is the result of setting each bit of X whose index is a member of list L to K. Indexing of bits is from 0, counting from the low order end. So if L is [2,4] and K is 0, then this instruction computes R by setting bits 2 and 4 of X to 0. If X is 31, then R is 11 in this case. X is thought of as standing for its infinite two's comlement representation. so 0 is an infinite vector of 0's, and -1 is an infinite vector of 1's.
R is the result of shifting X to the right M bits and then keeping only the rightmost N bits of that result, replacing all bits to the left of those N bits by 0. X is thought of as standing for its infinite two's comlement representation. so 0 is an infinite vector of 0's, and -1 is an infinite vector of 1's.
R is a bit vector of N 1 bits with infinitely many 0 bits to the left of them. In terms of numbers, R is 2N-1.
X an integer thought of as an infinite bit vector. If X > 0 (so the bit vector has infinitely many 0's) then K is the index of the rightmost 1 bit in X, and R is the result of setting that bit to 0 in X. If X < -1 (so the bit vector has infinitely many 1's) then K is the index of the rightmost 0 bit in X, and R is the result of setting that bit to 1. Indices are from the right (low order) end of the number, starting at 0.
If X is either 0 or -1, then this instruction fails with exception domainX("Cannot extract 1 bit from zero bit vector").
X an integer thought of as an infinite bit vector. If X > 0 (so the bit vector has infinitely many 0's) then K is the index of the leftmost 1 bit in X, and R is the result of setting that bit to 0 in X. If X < -1 (so the bit vector has infinitely many 1's) then K is the index of the leftmost 0 bit in X, and R is the result of setting that bit to 1. Indices are from the right (low order) end of the number, starting at 0.
If X is either 0 or -1, then this instruction fails with exception domainX("Cannot extract 1 bit from zero bit vector").
Check that the top X of the data stack satisfies 0 <= X < n.
If X < 0 or X >= n then this instruction fails with exception conversionX.
Subtract 1 from a natural number, but do not allow the result to be negative.
If N <= 0 then this instruction fails with exception domainX("predecessor of smallest").
Push a new empty nonshared box onto the data stack. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
B* : Box(T) |
Push a new empty shared box onto the data stack. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
B* : Box(T) |
Modify the data stack as follows.
R is true if box B is empty and false otherwise.
Modify the data stack as follows.
Store X into box B.
If this box has demons attached to it, run the demons. See ATTACH-DEMON.
When B is a computed box, this instruction performs an application of the assignment function of the computed box. It pushes a new frame on the call stack, and continues with that new frame. The value returned by that function is on the stack when the current function continues with the next instruction.
This is the same as ASSIGN, but it indicates that this is an initial content for the box. That is no different from ASSIGN for a shared box, but when B is a nonshared box it stores the result not only in the current thread's state but also in a global initial state, where any other thread can find the initial value of this box.
This is the same as ASSIGN, but it does not run demons associated with the box.
Modify the data stack as follows.
Make box B be empty. If there are any demons associated with B then run them. See ATTACH-DEMON.
When B is a computed box, this instruction performs an application of the assignment function of the computed box. It pushes a new frame on the call stack, and continues with that new frame. The value returned by that function is on the stack when the current function continues with the next instruction.
This is similar to MAKE-BOX-EMPTY, but does not run demons associated with the box.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
B* : Box(T) | X : T |
X is the current content of box B. If B is empty then this instruction fails with exception emptyBoxX.
This is the same as CONTENT but when it fails the exception is testX.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
B : Box(T) |
Box B is one of the standard boxes, given by name. The possibilities are as follows.
Standard boxes | |
---|---|
Box | Description |
precision | This nonshared box holds the precision for real numbers in decimal digits. |
stdin | This nonshared box holds the contents of the standard input (a list of characters). This string ends on a lazy value whose evaluation causes more characters to be read. If none are available, the thread that performed the read is blocked. |
stdout | This nonshared box initially holds a SysOutfile that is bound to the standard output of this process. |
traceSockets | This nonshared box initially holds false. If set to hold true, actions on sockets will be traced to the standard output. |
useCommas | This nonshared box initially holds false. If set to hold true, numbers are shown (by TO-STRING or TO-STRING-FORMATTED) with commas every three digits. |
cwd | This nonshared box holds the current working directory. |
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
(F,(S,L)*)* : (() -> Optional(T), (Optional(T) ->(), Natural -> Boolean)) | B* : Box(T) |
Build a computed box B. A computed box is simulated by functions.
F is a function that fetches (or simulates fetching) the content of a box. F() returns 0 if the box is empty and returns (v:1) if v is the content of the box.
S is a function that is used to set (or simulate setting) the value of a box. S(v:1) sets the content to v. S(0) makes the box empty.
L is a function that locks and unlocks the box.
Action of the lock/unlock function L | |
---|---|
L(0) | locks the box. Its return value is ignored. |
L(1) | unlocks the box. Its return value is ignored. |
L(2) | unlocks the box completely. Its return value is ignored. |
L(3) | returns true if the box is locked and false if the box is not locked. |
Modify the data stack as follows.
A is a list of N new empty nonshared boxes. A packed representation is used.
Modify the data stack as follows.
A is a list of N new empty shared boxes. A packed representation is used.
Modify the data stack as follows.
F is 0 if B is a nonshared box, 1 if B is a shared box, and 2 if B is a computed box.
Push the current state onto the state stack, so that it can be restored later.
Pop the state stack and set the state to the popped state.
Modify the data stack as follows.
Attach function F to box B as a demon. When an ASSIGN or EMPTY-BOX instruction performed, just after the assignment is performed F(u,v) is run with parameters u and v that indicate the former and new content of box B. Specifically, u is 0 if B was formerly empty, and is (c:1) if B formerly contained c. Similarly, v is <0 if box B was just emptied (by an EMPTY-BOX instruction) and is (d:1) if an ASSIGN instruction was done that put d into this box.
There is one exception: if an empty box is emptied, the demon is not run.
You can attach several demons to one box. The demons are evaluated in the same order in which they were attached.
Modify the data stack as follows.
N is an number that indicates the current address of box B. This can be used to refer to the box briefly. But be careful. A garbage collection can move a box. See SUPPRESS-COMPACTIFY if you need box ranks not to change.
Get the species Box(T) -> A stored at index k in the static environment. Modify the data stack as follows.
S is string that describes box B. The description is based on the rank of this box.
The species T is only used in some circumstances to determine the content species of the box. It is required when the showing the configuration so that the interpreter can determine which boxes should be shown.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
U : T |
U is new unbound nonshared unprotected unknown.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
U : T |
U is new unbound shared unprotected unknown.
Modify the data stack as follows.
U is new unbound nonshared protected unknown and K is its key. The key is required to bind the unknown.
Modify the data stack as follows.
U is new unbound shared protected unknown and K is its key. The key is required to bind the unknown.
Modify the data stack as follows.
If k = 0 then bind an unprotected unknown. The data stack is modified as follows, and unknown U is bound to X.Data stack | |
---|---|
before | after |
X : T | |
U* : T |
If k =/= 0 then modify the data stack as follows.
K must be the correct key for unknown U. Bind U to X.
This instruction fails with exception bindUnknownX if U is not an unbound unknown or if K is not the correct key for U, ir if the unknown is of the wrong kind for the instruction.
Modify the data stack as follows.
R is true if X is an unbound unknown, and is false otherwise.
Modify the data stack as follows.
R is true if X is an unbound protected unknown, and is false otherwise.
Modify the data stack as follows.
R is true if X is an unbound unprotected unknown, and is false otherwise.
Modify the data stack as follows.
R is true if X and Y are the same unbound unknown, and is false otherwise.
Tag an item with a species. Modify the data stack as follows and type stack as follows.
Exception. If X is an item that is already tagged with a species, then do not add another tag. Leave X as it is. In this case, the data stack and type stack are modified as follows.
|
|
Tag an item with a species. The species is the domain of a function species that is stored at index k in the static environment. Suppose that the species at index k is A -> B. Modify the data stack as follows.
Exception. If X is already tagged by a species then X is pushed, not (X:A). This instruction will not add another layer of species tag.
Get the species stored at index k in the static environment. It should be a function species, A -> B. Pop a number X from the data stack. If X has the form (Y:S) then replace X by Y, and ignore S.
Find the first species in the list (Natural, Integer, Rational, Real) that is consistent with both the representation of X and with species B, and call that species T. Species T is compatible with the representation of X if X could be the representation of a value of that species. Species T is compatible with species B if T <= B in the species hierarchy.
If T = B, or if X already has a species tag, then push X' = X. If T < B and X does not have a species tag then push X' = (X:T).
Modify the data stack as follows.
Remove the species tag from an item. Modify the data stack as follows and type stack as follows.
|
|
This instruction is intended to be used after applying an irregular function. Modify the data stack and type stack in one of the following ways.
|
|
|
|
This instruction gets A and B from the type stack. Species A must not contain any unbound variables, but B can contain unbound variables. This instruction adds constraint A <= B. If addition of that constraint makes the hierarchy inconsistent then this instruction fails with exception speciesX.
Adding the new constraint can cause variables in B to become bound. Any remaining unbound variables in B are bound to default values. If, after all variables in B have become bound, B = A, then the item on the data stack is not modified, as shown in the first pair of diagrams. But if it is found that A < B then the value on the top of the data stack is either tagged or retagged with species A, as shown in the second pair of diagrams.
Attach a nonnegative integer tag to an item.
Data stack | |
---|---|
before | after |
X | (X:n)* |
If X is () then the value pushed is just the integer n, not (():n).
Attach a nonnegative integer tag to an item.
If X is () then the value pushed is just the integer N, not (():N). If N < 0 then this instruction fails with exception domainX("Negative tag").
Get the integer tag of an item.
Data stack | |
---|---|
before | after |
(X:N)* | N** : Natural |
Data stack | |
---|---|
before | after |
N** : Integer | N** : Integer |
Data stack | |
---|---|
before | after |
X* | -1* |
If the item on top of the data stack is not an integer and is not a tagged value then this instruction replaces the stack top by -1.
The result R is true if X has the form (Y:n) or if X is n, where n is the instruction parameter, and R is false otherwise.
If the item on top of the data stack has tag n or is n then this instruction succeeds, and it pushes the value with the tag removed. An integer is treated as a tagged (). If the item on top of the data stack has any other form then this instruction fails with exception testX.
If the stack top contains a value that has an integer tag or that is a nonnegative integer then push a pair consisting of the tagged value and the tag. Otherwise fail with exception testX.
This instruction creates a function. The overall form is as follows.
Form of FUNCTION instrution | |
---|---|
FUNCTION L | |
instructions | These instructions compute the codomain species of the function and push that onto the type stack. |
BEGIN-BODY i | Begin the executable part. The number i tells an upper limit on the number of slots that are required in the local environment of the function. See specifying the size of the environment. |
instructions | These instructions are the body of the function. |
LOC-LABEL L | The end of the function. Notice that this label is the one that occurs in the FUNCTION instruction. |
The function that is created starts with its parameter on top of the data stack. It performs the instructions that follow the BEGIN-BODY instruction. When the function is finished, it should put its result onto the data stack and perform a RETURN or INVISIBLE-RETURN instruction. If it falls through at the LOC-LABEL L instruction without returning, results are undefined.
The function is given the same name as the current activation's name.
After the function is built it is pushed onto the data stack, and the computation that created the function continues running just after LOC-LABEL L.
Modify the data stack as follows, where F is the function that is created.
Data stack | |
---|---|
before | after |
F |
Example: Here is a function f(x) = x + x, where x must have species Integer.
FUNCTION 1 STD-SPECIES Integer BEGIN-BODY 1 BIND-AND-LEAVE 0 FETCH 0 PLUS RETURN LOC-LABEL 1
This instruction applies a function. It pushes a new frame onto the call stack and begins running a function in that new frame. When the function call is done, the function's result will be on top of the data stack.
If you imagine the computation of the function to be performed on the side, and ignore that computation, then the effect is to modify the data stack as follows.
Data stack | |
---|---|
before | after |
F* : S -> T | F(A) : T |
A : S |
However, what really happens is that a new frame is pushed onto the call stack, causing a new context to be entered. Function F runs in this new context until it performs a RETURN or INVISIBLE-RETURN instruction, causing its frame to be removed from the call stack, and the former context to be continued.
This instruction is similar in effect to an APPLY instruction. However, the new function's frame in the call stack replaces the current function's frame. This means that, when this function call returns, there is an implicit return from the current function call. So
TAIL-APPLYis equivalent to
APPLY RETURNHowever, a TAIL-APPLY is more efficient than an APPLY followed by a RETURN.
This is equivalent to n PAIR instructions followed by APPLY. However, it is more efficient.
This is equivalent to n PAIR instructions followed by TAIL-APPLY. However, it is more efficient.
This is equivalent to APPLY. However, the function that is called is given a short time limit to finish its work. If it does not finish, then its computation becomes deferred (building a promise to continue it) and the deferred value is pushed onto the stack.
For this instruction, a tagged value either has the form (V:N) where N is an integer, or is an integer itself. The tag of (V:N) is N, and the tag of N is N.
This instruction applies an equality testing function, E, to a pair of items, with some special rules. This instruction should only be used when the following are true.
E is only used, and only evaluated, if the answer could not be determined by using the assumptions about the behavior of E.
Return to the caller of a function. This instruction should be performed with the function's result on top of the data stack.
Same as a RETURN, but for a function that wants to remain hidden to the debugger.
Push the address of the instruction just after this one onto the local-return-address stack, and jump to label L.
Pop the local-return-address stack, and jump to address that is popped.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X | R* : Boolean |
The result R is true if X is not a promise, so is known at least to its top level structure. R is false if X is a promise.
This is almost the same as TEST-NOT-LAZY, but it occasionally pushes a result of false even when X is not a promise.
This instruction creates a promise. The overall form is as follows.
Form of LAZY instruction | |
---|---|
LAZY L | |
instructions | These instructions push the species of the value that is to be computed onto the type stack. |
BEGIN-BODY i | Begin the executable part. The number i tells an upper limit on the number of slots that are required in the local environment of the instructions to follow. See specifying the size of the environment. |
instructions | These instructions are the body of the promise. |
LOC-LABEL L | The end of the body. Notice that this label is the one that occurs in the LAZY instruction. |
The promise that is created will be evaluated, when needed, by performing the instructions in the body. Those instructions should finish by performing a RETURN or INVISIBLE-RETURN with the result on top of the data stack. If evaluation of the body falls through at the LOC-LABEL L instruction without returning, results are undefined.
The result of evaluating a promise is memoized. After evaluation, the promise is destroyed and replaced by the computed value.
In a little more detail, what you really get is a control tree representing one or more threads that are attached to this evaluation. When the control tree is created, it gets a copy of the thread-local data structures that were in effect when the promise was created. When a promise is evaluated, it runs effectively as a separate thread, except that it is attached to the promise, and is not in the forest of control trees managed by the interpreter.
It is possible for the body to perform forks, and so for the control tree associated with a promise to have several threads. As soon as one of those threads produces a value, that value is chosen as the value of the promise, and all other threads in the control tree for this promise are destroyed.
If all threads for a promise fail, then evaluation of the promise fails with the exception associated with the last thread to fail. The fact that this computation failed is memoized, and another attempt to evaluate the same promise will immediately fail with the same exception.
After the promise is built by the LAZY instruction, it is pushed onto the data stack, and the computation that created the promise continues running just after LOC-LABEL L.
Modify the data stack as follows, where P is the promise that is created.
Data stack | |
---|---|
before | after |
P |
When a promise is evaluated, the interpreter remembers the result, and avoids recomputing the result of the promise. But if the promise is created using LAZY-RECOMPUTE, then the interpreter is given permission not to remember the result, and instead to recompute it whenever it is needed. Otherwise, LAZY-RECOMPUTE is the same as LAZY.
It is up to the interpreter when and whether to take advantage of this permission.
This instruction creates a promise. The overall form is as follows.
Form of LAZY-LIST instruction | |
---|---|
LAZY-LIST L | |
instructions | These instructions push the species of the list that is to be computed onto the type stack. |
BEGIN-BODY i | Begin the executable part. The number i tells an upper limit on the number of slots that are required in the local environment of the instructions to follow. See specifying the size of the environment. |
instructions | These instructions are the body of the promise. |
LOC-LABEL L | The end of the body. Notice that this label is the one that occurs in the LAZY-LIST instruction. |
A LAZY-LIST instruction creates a promise that works in a similar way to the promise created by a LAZY instruction. The body should put a value on the stack and do a RETURN to produce the value.
The difference between LAZY and LAZY-LIST is in how the value is produced. The body of a LAZY-LIST promise typically forks several threads. As each thread produces a result, that result is accumulated into a list, and that list is the value of the promise.
In more detail, when evaluation of the promise starts, the body runs until the first of the threads produces a value, say V. A list is created whose head is V and whose tail is a promise with the remaining control tree attached to it. When (and if) that tail is computed, computation resumes until another thread produces a value. The control tree is associated with the promise, and will not be run until the promise is forced.
If the body of a LAZY-LIST promise does not produce any values, then the lazy list has value [].
The result of forcing a LAZY-LIST promise is memoized.
After the promise is built by the LAZY-LIST instruction, it is pushed onto the data stack, and the computation that created the promise continues running just after LOC-LABEL L.
Modify the data stack as follows, where P is the promise that is created.
Data stack | |
---|---|
before | after |
P |
When a promise is evaluated, the interpreter remembers the result, and avoids recomputing the result of the promise. But if the promise is created using LAZY-LIST-RECOMPUTE, then the interpreter is given permission not to remember the result, and instead to recompute it whenever it is needed. Otherwise, LAZY-LIST-RECOMPUTE is the same as LAZY-LIST.
It is up to the interpreter when and whether to take advantage of this permission.
Evaluate the top of the data stack to the point where it is no longer a promise. (There might be more promises deeper in the structure after evaluation.) Leave the result on top of the data stack.
It is not necessary for you to force promises explicitly. All instructions that need values will force promises where necessary. This instruction is only provided so that you can perform a force at a desired time if that is what you need to do.
Evaluate the top of the data stack fully, so that there are no more promises inside its structure. This instruction sees only lists and pairs as structures. It will not force the content of a box.
Fetch the item stored at index n of the local environment. The local environment is the first one in the environment chain. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X |
where X is the item found in the environment.
This is similar to a FETCH instruction, but the interpreter is given permission to destroy this binding after performing the fetch. Any future fetch of from this index before it has been stored again yields an undefined result. (When the interpreter is run in debug mode, it will not destroy the binding.)
This is similar to a FETCH instruction, but the fetch is from the environment in the current chain that is k frames from the front of the list. This moves out k scopes.
This is similar to a LONG-FETCH instruction, but the interpreter is given permission to destroy this binding after performing the fetch. Any future fetch of from this index before it has been stored again yields an undefined result. (When the interpreter is run in debug mode, it will not destroy the binding.)
Fetch the item stored at index n of the static environment (the one at the end of the environment chain). Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X |
where X is the item found in the static environment.
Pop a species T from the type stack. There must be an @ID declaration just after global label L. Let name be the name defined there. Fetch the binding of name of species T directly from the global definition table, and push it onto the data stack.
If there is no such definition, this instruction fails with exception noDefX(name:T).
Pop a species List(Char) -> T from the type stack. Pop a string name from the data stack. Fetch the binding of name of species T directly from the global definition table, and push it onto the data stack. The data stack is modified as follows.
|
|
where X is the value obtained from the global definition table.
If there is no such definition, this instruction fails with exception noDefX(name:T).
Pop a species T and a string name from the data stack. Fetch the binding of name of species T directly from the global definition table, and push it onto the data stack, as a tagged value, tagged by species T. The data stack is modified as follows.
where X is the value obtained from the global definition table.
If there is no such definition, this instruction fails with exception noDefX(name:T).
Store the item on top of the data stack at index n in the local environment (the first one in the environment chain). Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X |
where X is the item that is stored. (name) is optional. It is the name of this local variable, and is only used for debugging. If omitted, it is taken to be an empty string.
A BIND instruction is followed by a sequence of instructions that build the species of what is being stored by the BIND instruction, leaving the species on top of the type stack, followed by an END-TYPE instruction. The species is only used for debugging, and can be omitted. However, the END-TYPE instruction is required.
Example. to store the top of the stack at offset 2 in the local environment, and to indicate that this offset has name x:
BIND 2 (x) END-TYPEThe above definition omits species information about x. If you know that x has type Integer, you can write
BIND 2 (x) STD-SPECIES Integer END-TYPEand if you have stored the species of x at offset 3 in the static environment, you can write
BIND 2 (x) GLOBAL-FETCH-SPECIES 3 END-TYPE
This is similar to a BIND instruction, but the interpreter is given permission not to do the store, because this value will never be used. (When the interpreter is run in debug mode, it will perform the store.) Like a BIND instruction, it must be followed by (optional) instructions to build a type and an END-TYPE instruction.
This is similar to a BIND instruction, but it does not remove the item that was stored from the top of the data stack.
Data stack | |
---|---|
before | after |
X | X |
This is similar to a BIND-AND-LEAVE instruction, but it indicates that the value stored will never be fetched. If it is fetched, the results are undefined. (When run in debug mode, the interpreter will perform the store.)
This is similar to a BIND instruction, but does not provide a name, and indicates a change of a value that was already stored. Also, a REBIND instruction is not followed by type-building instructions or an END-TYPE. A REBIND is only allowed to alter the value stored in the environment, not the species.
This is similar to a BIND-AND-LEAVE instruction, but does not provide a name, and indicates a change of a value that was already stored. No type-building instructions are used.
This is similar to a DEAD-BIND-AND-LEAVE instruction, but does not provide a name, and indicates a change of a value that was already stored. There are no type-building instructions.
Form of DEFINE instruction | |
---|---|
DEFINE L n (name) | |
instructions | These instructions compute the species of the item being defined and push that onto the type stack. |
BEGIN-BODY i | Begin the executable part. The number i tells an upper limit on the number of slots that are required in the local environment of the function. See specifying the size of the environment. |
instructions | These instructions are the body of the definition. They compute the value. |
LOC-LABEL L | The end of the definition. Notice that this label is the one that occurs in the DEFINE instruction. |
A promise is created, and that promise is stored in the local environment at index n. The instructions in the body should finish by putting the value being defined on top of the data stack and performing a RETURN or INVISIBLE-RETURN instruction. If evaluation of the body falls through at the LOC-LABEL L instruction without returning, results are undefined.
After the DEFINE is performed, and the computation that did the definition continues running just after LOC-LABEL L.
Destroy all bindings beyond the first n bindings in the local environment (the first one in the environment chain). Any future use of these bindings before storing them again yields undefined behavior.
Indicate a local label inside a declaration. The local label number can be preceded by a : as an indication that it is a local label. All instructions that refer to local labels can precede the label number by a :.
Jump to the instruction just after local label L.
This is the same as n PAIR instructions followed by GOTO L.
Pop the data stack. If the popped value is false or 0 or [], then jump to local label L. Only an integer value 0 is recognized as an indication 0 here. If the popped value is anything else, do nothing.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X* |
This is similar to GOTO-IF-FALSE, but does not remove the tested value from the data stack.
Look at the top of the data stack. If it is false or 0:Integer or [] then leave the data stack alone and jump to local label L. But if the data stack has another value on it, then pop the data stack and continue with the instruction after this one.
Look at the top of the data stack. If it is false or 0:Integer or [] then pop it from the data stack and continue with the next instruction. But it the data stack has another value on it, then do not pop the data stack, and jump to local label L.
If the top of the data stack is false then fail with standard exception name.
Data stack | |
---|---|
before | after |
X* : Boolean |
Fail with standard exception name.
Pop an exception from the data stack and fail with the popped exception.
Data stack | |
---|---|
before | after |
X* : Exception |
Fail with the exception that is on the top of the exception stack. If that exception was pushed using a PUSH-EXCEPTION-PROPAGATABLE instruction, then the failure will appear to have been caused at its original location, not at the location of the PROPAGATE-FAILURE instruction.
Add an exception handler to the control. The current thread is a leaf in the control tree. This adds a control node just above the current thread that will catch a failure by jumping to local label L.
The new control node remembers all of the thread-local data structures, and those structures are returned to their values as they were when the TRY instruction was performed when a failure is caught.
Opt consists of up to two characters indicate characteristics of the new control node. You can select or not select each of the following options.
Try options | |
---|---|
Option | Meaning |
t | This option controls which exceptions this node will catch.
|
e | This option determines how the new node responds to the case
where a thread forks while running beneath it, but not beneath
another exception handling node.
|
This is the same as a TRY with a null option, but it avoids making copies of the thread-local data strutures except for the data stack. As a result, it is more efficient than a TRY instruction. However, it has some restrictions on when it can be used. A quick-try can only be performed when the type stack is empty. The program must not perform any instructions that modify the thread-local data structures, excluding the data stack, before it performs a TRY-THEN instruction or fails. The following instructions are not allowed to be performed betweeen a QUICK-TRY and a TRY-THEN.
Remove the exception handling node that is just above the current thread's node in the control tree. This indicates exit from that part of the code that is protected by this exception handler.
This can only be performed immediately after catching a failure. It pushes the exception associated with the failure onto the exception stack.
This is the same as PUSH-EXCEPTION except it pushes a little bit more information with the exception. The additional information is used by a PROPAGATE-FAILURE instruction to know who caused the exception.
Pop the exception stack. Do nothing with the popped exception.
Push the exception that is on top of the exception stack onto the data stack. Do not modify the exception stack.
Push the current trap set (the one on top of the trap stack) onto the trap stack, duplicating it. This allows the top trap set to be modified, and the old one to be recovered later.
Pop the trap stack. This causes the current trap vector to become the one that was beneath the top one on the trap stack.
Pop an exception set S from the data stack. Modify the current trap set so that all exceptions in set S are trapped. Do not modify the status of any exceptions not in S. The data stack is modified as follows.
Data stack | |
---|---|
before | after |
S** : ExceptionSetSpecies |
Pop an exception set S from the data stack. Modify the current trap set so that all exceptions in set S are not trapped. Do not modify the status of any exceptions not in S. The data stack is modified as follows.
Data stack | |
---|---|
before | after |
S** : ExceptionSetSpecies |
Pop an exception E from the data stack. Push true if E would be trapped in the current context, and false if E would not be trapped.
Push the exception defined at global label L (by an @EXCEPTION declaration) onto the data stack. This is used for structureless exceptions.
Data stack | |
---|---|
before | after |
E* : ExceptionSpecies |
Push an exception constructed using the exception constructor defined at global label L (by an @EXCEPTION declaration) with the current stack top as an argument onto the data stack.
Data stack | |
---|---|
before | after |
X | E* : ExceptionSpecies |
If the constructor defined at label L is C, then E is C(X). For example, if X is "orange apple" and the exception constructor defined at label L is domainX, then E is domainX("orange apple").
Get the information associated with an exception.
Data stack | |
---|---|
before | after |
E* : ExceptionSpecies | X |
Check to see that exception E is either the structureless exception defined at global label L, or is an exception that is constructed using the exception constructor defined at global label L. If it is not, then fail with exception testX.
If E is of the correct kind, then push the information X associated with E. For example, if E is domainX("orange apple") then X is "orange apple". If E is a structureless exception, push ().
Check whether an exception has a given class.
Check to see that exception E is either the structureless exception defined at global label L, or is an exception that is constructed using the exception constructor defined at global label L. If it is, then push R = true. If not, then push R = false.
Modify the data stack as follows.
D is the description associated with exception E. The description is part of the definition of an exception or exception constructor.
Modify the data stack as follows.
S is a string that represents exception E. If E is testX, then S is "testX". If E is domainX("orange apple") then S is "domainX(\"orange apple\")"
If L is a nonnegative integer then push the exception set defined at global label L. If L is a name then push the standard exception set by that name. The possible names are as follows.
Standard exception sets | |
---|---|
allXS | The set of all exceptions |
shouldTrapXS | The set of all exceptions that are very serious, and from which recovery is difficult. This includes memoryX, limitX, domainX(s) and infiniteLoopX. |
normallyTrappedXS | The set of all exceptions that are initially trapped. |
normallyBenignXS | The set of all exceptions that are initially not trapped. |
terminatingXS | The set of all exceptions that indicate termination of a thread. This includes terminateX, detachX and endThreadX. |
Data stack | |
---|---|
before | after |
S** : ExceptionSetSpecies |
R is true if exception sets A and B have exactly the same members, and false otherwise.
R is true if exception sets A and B have exactly the same members, and false otherwise.
R is true if exception E is a member of exception set S, and is false otherwise.
R is the complement of exception set A. It contains all exceptions not in A.
Data stack | |
---|---|
before | after |
B** : ExceptionSetSpecies | R** : ExceptionSetSpecies |
A** : ExceptionSetSpecies |
R is the union of exception sets A and B.
Data stack | |
---|---|
before | after |
B** : ExceptionSetSpecies | R** : ExceptionSetSpecies |
A** : ExceptionSetSpecies |
R is the intersection of exception sets A and B.
R is a string of the form [a,b,...] where a, b, ... are the constructors of exceptions that are in exception set S.
Split the current thread into two threads. Each thread has a copy of all of the thread-local data structures, and each is initialized the same, with the exception of the instruction counter. The first thread has it instruction counter set to the instruction just after this BACKTRACK instruction. The second thread has its instruction counter set to the instruction after local label L.
A thread is a leaf of a control tree. This instruction splits the leaf by introducing a node above the leaf. The new node is marked as a backtrack node, and has the first and second threads as its children.
Backtrack nodes are handled as follows. As long as there is any active thread in the left subtree of a backtrack node, the right subtree will wait, and will not be performed. If the left subtree ever becomes empty (all threads in it have terminated) then the backtrack node is replaced by its right subtree, and that tree will be allowed to run.
Split the current thread into two threads. Each thread has a copy of all of the thread-local data structures, and each is initialized the same, with the exception of the instruction counter. The first thread has it instruction counter set to the instruction just after this BACKTRACK instruction. The second thread has its instruction counter set to the instruction after local label L.
A thread is a leaf of a control tree. This instruction splits the leaf by introducing a node above the leaf. The new node is marked as a fork node, and has the first and second threads as its children.
Fork nodes allow their children to run concurrently. Each subtree is updated as the corresponding threads run. If either subtree ever becomes empty, the fork node is removed and replaced by its other child.
Push a cut-mark node just above the current thread's node in the control tree. The cut-mark mode is used in CUT instructions.
Remove the closest cut-mark node above the current thread's node from the control tree.
This instruction scans up the control tree from the current thread to the first cut-mark node. At each fork or backtrack node that it encounters, it terminates all threads in the subtree that is not in direction of the current thread. Termination is achieved by the same means as a TERMINATE instruction. A terminate message is sent to each one.
The data stack is modified as follows.
Cause the current thread to pause for N milliseconds. Other threads that are enabled can run while this thread pauses.
Cause the current thread to yield to other threads. This is used if the current thread knows that it cannot make progress until another thread runs, so does not want to do anything for a short time.
This is the same as PAUSE, but it indicates that this thread has made no progress since the last time it paused or repaused. If all threads repause, and there are no external triggers that might release one of the threads, the interpreter will presume that the program has deadlocked.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
A** : Natural |
Send a terminate request to all threads that have affiliation A in their affiliation sets. When honored, a terminate request causes the thread to fail with exception terminateX. See BEGIN-NOTERMINATE for special circumstances.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
A** : Natural |
Send a suspend request to all threads that have affiliation A in their affiliation sets. Once suspended, a thread will not run until it is resumed. But see BEGIN-NOSUSPEND and BEGIN-NOTERMINATE for special circumstances.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
A** : Natural |
Resume all threads that have affiliation A in their affiliation sets and that have been suspended.
Cause the current thread to suspend itself. It will not continue until another thread resumes it.
Add 1 to the current thread's nosuspend count. When the nosuspend count is positive, the thread cannot be suspended. Any request to suspend it will be remembered, and will be honored when the thread enters a state where it can be suspended.
If a thread receives a suspend request while it cannot be suspended, and receives a resume request before it honors the suspend request, then the suspend request is removed.
Subtract 1 from the current thread's nosuspend count. If the count was already 0, leave it 0.
If this instruction causes the nosuspend count to become 0, it is possible that a deferred suspend request will be honored now.
Add 1 to the current thread's noterminate count. When the noterminate count is positive, the thread cannot be suspended or terminated. Any request to suspend or terminate it will be remembered, and will be honored when the thread enters a state where the request can be honored.
Subtract 1 from the current thread's noterminate count. If the count was already 0, leave it 0.
If this instruction causes the noterminate count to become 0, it is possible that a deferred suspend or terminate request will be honored now.
Modify the data stack as follows.
R is true if there is any other active thread that has affiliation A in its affiliation set.
The active threads are those that are found in the forest of control trees that the interpreter is running. It does include threads waiting at backtrack nodes for other threads to finish. But it does not include any threads that are attached to computations of deferred values.
Move the current thread's control node, making it a root in the forest of control trees that the interpreter maintains. Replace the old node by a node that will immediately fail with exception detachX.
This is the same as DETACH, but it empties the data stack and the call stack. A return from the function that performs this instruction will terminate the thread.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
A** : Natural |
Add affiliation A to the current thread's affiliation set. But refuse to add affiliation 1, since that is managed automatically.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
A** : Natural |
Remove affiliation A from the current thread's affiliation set. But refuse to remove affiliation 1, since that is managed automatically.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
L** : Natural |
Push a list of the affiliations of the current thread.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
L** : Natural |
Install list L as the current affiliations of the current thread. Whether the current thread has affiliation 1 is independent of L. That affiliation is managed automatically.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
B* : Box(T) | () |
Pop box B from the data stack. If B is a nonshared box, do nothing else. Otherwise, attempt to lock B. The possible outcomes are as follows.
If no thread currently has box B locked, then the current thread locks it, and continues. Box B is added to the locked-box list.
If the current thread already has box B locked, then the lock count for this thread for box B is incremented. (Another copy of B is added to the locked box list.) A thread can have an arbitrarily large lock count for any given box.
If some thread has this box locked, but the current thread does not have it locked, then this thread waits until box B is unlocked, at which time it can continue, and it completes this instruction.
When B is a computed box, this instruction performs an application of the locking function of the computed box. It pushes a new frame on the call stack, and continues with that new frame. The value returned by that function is on the stack when the current function continues with the next instruction.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
B* : Box(T) | () |
Pop box B from the data stack. Perform the following action.
If B is a nonshared box, or if B does not occur in this thread's list of locked boxes, then do nothing.
If the current thread has box B in its list of locked boxes, then remove one of the copies of B from that list. If this thread's lock count for B becomes 0, and no other threads have B locked, and if any thread has attempted to lock B, then allow one of those threads to lock B.
Selection of a thread to let run is done in a fair manner in the sense that no thread that is trying to lock the box will wait indefinitely while other threads are repeatedly given the lock. Any given thread will eventually be chosen.
When B is a computed box, this instruction performs an application of the unlocking function of the computed box. It pushes a new frame on the call stack, and continues with that new frame. The value returned by that function is on the stack when the current function continues with the next instruction.
This is similar to UNLOCK-BOX, the only difference being that it removes all copies of B from the current thread's list of locked boxes.
Data stack | |
---|---|
before | after |
() |
Make the current thread's list of locked boxes be empty. This has the same effect as performing UNLOCK instructions until all of the locks are removed.
R is true if box B is in the current thread's list of locked boxes at least once.
Open a file and push it onto the data stack. Modify the data stack as follows.
Path is the path name of the file to open, relative to the current thread's working directory. F is a SysOutfile that can be used to write to the listed file. List M indicates mode information.
If it is not possible to open the file then this instruction fails with exception noFileX(path). In the special case where the open failed because path is a directory, the exception is fileTypeX(path).
Close a SysOutfile object. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
F* : SysOutfile |
Close file F. Any subseqent attempts to write to F will fail. Closing a file that is already closed has no effect.
Modify the data stack as follows.
Print character c on file F.
If file F has been closed, this instruction fails with exception closedFileX.
Modify the data stack as follows.
Print all of the strings in list L to file F, in the order in which they occur in L.
If file F has been closed, this instruction fails with exception closedFileX.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
F* : SysOutfile |
Output is buffered. Flush the buffer of file F.
Produce a string that describes a SysOutfile. Modify the data stack as follows.
F is a SysOutfile object, and S is a string that describes it, such as "outfile(name)".
Modify the data stack as follows.
Open file path for reading. The path is relative to the current thread's working directory. Push a string R that is the contents of the file.
M is a list of modes.
The file is not actually read immediately. Reading only begins when part of the file is examined. Only part of the file is read at a time, with the remainder being lazy. More will be read as it is used. Indexing into the file string using SUBSCRIPT or SUBLIST might cause a seek to be done on the file to find a position to continue reading.
If it is not possible to open this file for reading, this instruction fails with exception noFileX(path). In the special case where path is a directory, it fails with exception fileTypeX(path).
Change the current directory. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
S** : List(Char) |
S is the path name of a directory to remove, relative to the current thread's working directory. Make S be the working directory of the current thread. This modifies the content of the cwd box, and also checks that it is possible to select S as the working directory.
This instruction fails with exception fileTypeX(S) if S is not a directory, and with exception noFileX(S) if it was not possible to select S as the current working directory for some other reason.
Rename a system file. Modify the data stack as follows.
R and S must be path names relative to the current directory. Rename file S to R. Fail with exception noFileX(S) if this cannot be done, either because S does not exist or because this process does not have permission to do the renaming.
Remove a system file. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
S** : List(Char) |
S is the path name of a file, relative to the current thread's working directory. Unlink system file S from the file system, if possible. Fail with exception noFileX(S) if this cannot be done, either because S does not exist or because this process does not have permission to do the unlink.
Remove a system directory. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
S** : List(Char) |
S is the path name of a directory to remove, relative to the current thread's working directory. Remove system directory S from the file system, if possible. Fail with exception noFileX(S) if this cannot be done, or with exception fileTypeX(S) if S is not a directory.
This instruction will only remove a directory if the directory is empty. It fails if the directory is not empty.
Create a system directory. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
S** : List(Char) |
S is the path name of a directory to create, relative to the current thread's working directory. Create system directory S from the file system, if possible. Fail with exception noFileX(S) if this cannot be done.
Read a directory. Modify the data stack as follows.
S is the path name of a directory, relative to the current thread's working directory. L is a list of the names of all files in directory S. The names are not full path names, but just the names local to this directory, but excludes "." and "..". Fail with exception noFileX(S) if this cannot be done, or with exception fileTypeX(S) if S is not a directory.
Create a symbolic link. Modify the data stack as follows.
F and S are path names relative to the current thread's working directory. Create a symbolic link called S that refers to existing file or directory F. Fail with exception noFileX(S) if the link could not be created.
Read a symbolic link. Modify the data stack as follows.
S is a path name relative to the current thread's working directory. A is the file or directory to which symbolic link S refers. Fail with exception noFileX(S) if S does not exist and fileTypeX(S) if S is not a symbolic link.
Get information about a file. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
S** : List(Char) | R** : (Natural, (Boolean, (Natural, (Natural, (Boolean, (Boolean, Boolean)), (Natural, Natural))))) |
S is a path name relative to the current thread's working directory. R is (status, is_lnk, size, mtime, (read-perm, write-perm, exec-perm), owner, group) where
Status is
0 | if the file does not exist |
1 | if the file is a regular file |
2 | if the file is a directory |
3 | if the file is some other file |
Is_lnk is true if the file is a symbolic link, and false otherwise. Is_lnk can be true even when status is 0, if this is a symbolic link that points to a nonexistent object.
Size is the file size in bytes, or 0 if the file does not exist.
Mtime is the modification time of the file, in seconds since 0:00 GMT Jan 1 1970, or is 0 if the file does not exist.
Read-perm is true if the current process has read permission to htis file.
Write-perm is true if the current process has write permission to this file.
Exec-perm is true if the current process has execute permission for this file.
Owner is the owner of this file.
Group is the group of this file.
Get the value of an environment variable. Modify the data stack as follows.
S is the name of an environment variable. V is the value associated with that environment variable in the system environment. This instruction fails with exception notFoundX if there is no such environment variable.
Modify the value of an environment variable. Modify the data stack as follows.
String binding must be of the form "name=val", and n must be at least as large as the length of binding. Modify the system environment so that variable name has value val. If there is no current binding of name, one is added.
Get the entire system environment. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
L** : List(List(Char), List(Char)) |
L is a list of pairs [(name1, val1), (name2, val2), ...]. indicating that name1 is bound to val1, etc. in system environment.
Get the current time. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
N is a current time as a number of milliseconds since 0:00 GMT Jan. 1 1970.
Back up a string on disk. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
info : (List(Char), (List(Char), (Natural, (Natural, Box(Boolean))))) | S : List(Char) |
This instruction is used to cache a string in a file so that the string can be removed from memory and recovered later from the file.
Info is a tuple (val,(fname,(offset, (mode, (ready))))) where
val | is a string |
fname | is a file path name (a string) |
offset | is an integer offset in file fname |
mode | is an open mode. It is 0 for opening in text mode and 4 for opening in binary mode. |
ready | is a shared box that is used to tell when the file is ready to read. If ready is not a nonshared box, then this instruction fails with exception domainX("need shared box for file-ref-string").
Push, in place of info, a value S that is the same as val, but that indicates that it can be recovered from file fname by opening it with the given mode and seeking to the given offset.
Warning. This instruction does not write any information into file fname. It does not attempt to open this file at all. It is the responsibility of some other part of the program to write the file. Box ready is used to tell when the writing has been completed. If ready contains false or is empty, the file has not been written, and cannot be used yet. When the program is done writing the file, it sets ready to hold true to indicate that the file is ready to be read, when needed.
When the file is ready, the garbage collector might remove S from memory, preferring to read it back from the file if necessary later.
Info is evaluated up to the point where its parts (val, fname, offset, mode, ready) are available. All parts except val are evaluated. String val is not evaluated by this instruction.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
L** : List(List(Char)) |
L is a list of all files that are currently being read by the interpreter. Each file name is a full path name. These files might be open for reading, and might be open in a lazy way where they will be opened later if information from the file is needed.
Get the interpreter's process number. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
N is the process number of the interpreter.
Signal a process. Modify the data stack as follows.
N is the process id number of a system process, and S is a signal number. Send signal S to process N.
Signal a process. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
opt* : Natural | R : ((), List(Char), List(Char), List(Char), Natural, Natural) |
L** : List(List(Char)) |
L is a list of strings representing a command to the system, as an argument vector. The first string in list L is the command to run. It is a path name, but the command to run is found using the system PATH variable.
Create a new process and run command L in that process. This instruction does not wait for the other process to terminate. It returns as soon as the other process has been created.
Opt is a list of options for running the command. Each member of opt has one fo the following forms. (S:n) indicates a tagged value with integer tag n.
Options for CREATE-PROCESS | |
---|---|
(S:0) | Run the command with its standard input containing string S. |
(S:1) | Run the command with its standard input drawn from the system file whose name is S. S is a string that is a path name relative to the current thread's working directory. |
(S:2) | Run the command with its standard output redirected to the system file called S. S is a string that is a path name relative to the current thread's working directory. If this file already exists, truncate (empty) it before writing. |
(S:3) | Run the command with its standard output redirected to the system file called S. S is a string that is a path name relative to the current thread's working directory. If this file already exists, append the command output to the end of what is currently in file S. |
4 | Merge the standard error output into the standard output. |
5 | Ignore the both the standard error and the standard output. |
If neither option (S:0) nor (S:1) is given, then the standard input of the new process will be empty. If more than one of (S:0) and (S:1) is given, the first one in list opt is used.
The result R is a tuple (interact, out, err, rest, status, pid) where the components are as follows.
Results of CREATE-PROCESS | |
---|---|
interact | This is an item that MUST be evaluated in order to interact with the new process. It is the program's responsibility to force evaluation of it. Failure to evaluate this will cause undetermined results, including possibly failure of the new process to finish. When interact is finished evaluating it produces () as its result. |
out | If none of the options (S:2), (S:3)
or 5 are present in opt,
then out is a lazy string that contains what is
written to the
standard output by the other process. As more information is
written by the process, it shows up in this string.
The string is terminated by an unknown until the process
has finished, so that an attempt to read beyond the end of
what has been written will cause a thread to wait.
If options (S:2) or (S:3) are given, then the first one of those in the option list is used. In this case, out is an empty string, since the output was redirected to a file. If neither (S:2) nor (S:3) is in opt, but opt contains 5, then out is an empty string. In this case, the standard output is ignored. |
err | If neither of options 4 or 5 are in opt, then err is what the command process wrote to its standard error output. Like out, it is ended by an unknown until the process terminates. If option 5 is present, then the standard error is ignored, and err is an empty string. If option 4 is in opt, but option 5 is not in opt, then the standard error output is merged into the standard output, and err is an empty string. |
rest | If option (S:0) occurs (before any (S:1) option)
then
rest is an unbound unknown when this instruction returns.
When the new process terminates, that unknown is
bound to the part of string S that the command process
did not read.
If option (S:0) is not in opt or is preceded by (S:1), then rest is an empty string. |
status | Status is initially an unbound unknown. When the new process terminates, this unknown is bound to the termination status (an integer) of the process. |
pid | Pid is the process id number of the command process. |
If the command could not be created then this instruction fails with exception systemX(info) where info is a string that describes the problem. If there is a problem opening the files for standard input or standard output, then this instruction fails with exception noFileX(name), where name indicates the file that could not be opened.
Create a new socket. Modify the data stack as follows.
I is an IP address (a 32 bit integer, in network byte order), P is a port number and M is a list of modes. Push a socket that is connected to port P on host I, opened with mode M.
Fail with exception networkX if the connection fails.
Close an open socket. Modify the data stack as follows.
Data stack | |
---|---|
before | after |
sock* : Socket |
Close socket sock. Any subseqent attempts to write to F will fail. Closing a socket that is already closed has no effect.
This instruction is the same as CLOSE-OUTFILE.
Modify the data stack as follows.
Pop sock, which must be an open socket. Read some characters from sock and push them onto the stack, as a string S. If there is any data available, then S will be a nonempty string. Its length is not determined. If an end-of-file is encountered immediately, fail with exception listExhaustedX.
If there is no information available from socket sock, then this instruction causes the thread that performed it to block until some information is available.
Fail with exception networkX if the read fails because of a network problem.
Modify the data stack as follows.
Pop sock, which must be an open socket. Read some characters from sock and push them onto the stack, as a string S. If there is any data available, then S will be a nonempty string. Its length is not determined. If an end-of-file is encountered immediately, fail with exception listExhaustedX. If there is no information available immediately, but no end-of-file has been encountered either, then S is an empty string. A later read might yield more data.
Fail with exception networkX if the read fails because of a network problem.
Modify the data stack as follows.
Write a (not necessarily proper) prefix of string S to socket sock. Typically, that will be all of S, but it might not be. R is the suffix of S that was not written. For example, if S is "abcdefghi" and this instruction chooses only to write the first 5 characters of S to the socket, then R will be "fghi". If all of S is written then R is an empty string.
This instruction will write at least one character to the socket. If the socket is not ready to accept any more characters, then this instruction causes the thread that performed it to block until it can perform a write, and write at least one character to the socket.
Fail with exception networkX if the write fails because of a network problem.
This is similar to WRITE-SOCKET-BLOCK, but if it the socket is not ready to accept any characters then no characters are written, and R = S.
Modify the data stack as follows.
As sockets are created they are numbered, and each gets a different number. N is the number of socket sock.
Modify the data stack as follows.
This instruction performs a DNS lookup. If S name of an internet host then I is its IP address, as a 32 bit integer in network byte order.
If the host does not exist, then this instruction fails with exception hostNameX(S). If there is a problem performing the lookup then this instruction fails with exception networkX.
Modify the data stack as follows.
This instruction performs a reverse DNS lookup. I is an IP address, as a 32 bit integer in network byte order. S is a corresponding host name.
If the host does not exist or information on it is not available, then this instruction fails with exception hostNameX(I') where I' is a string that describes I. If there is a problem performing the lookup then this instruction fails with exception networkX.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
B* : Box(Boolean -> ()) |
B must be a shared box. This instruction registers B with the garbage collector. When box B is collected by the garbage collector, the following is done.
If box B is empty the no additional processing is done. The garbage collector destroys the box.
If box B is being collected during a normal garbage collection, and B contains function F, then a new detached thread is created that runs F(true). Box B is not destroyed, but it is removed from the registered boxes before running F.
If box B is being collected during clean up at the end of the program, and B contains function F, then a new detached thread is created that runs F(false). Box B is not destroyed, but it is removed from the registered boxes before running F. It is possible for F to register new boxes with the garbage collector, causing more rounds of garbage collection to be performed for clean up. The interpreter will only perform a few phases of this before it stops the program, even though there remain registered boxes that have not had their functions run.
Indicate that the code that follows this instruction is from source line n.
L should be a global label that occurs prior to the current instruction's location, and that labels an @ID declaration. Indicate that the section of code to follow should be called by the name defined at that @ID declaration. This is used for debugging and for naming functions.
L should be a global label that occurs prior to the current instruction's location, and that labels a @STRING declaration. Indicate that the section of code to follow should be called by the name defined at that @STRING declaration. This is used for debugging and for naming functions. The string at label L must not contain any null characters.
Add 1 to the view flag. See LAZY-TO-STRING, below.
Subtract 1 from the view flag, but do not change it if it was 0.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X | S** : List(Char) |
If the view flag is nonzero and X is a promise or an unknown then this instruction pushes S as shown. S is a string that describes this promise or unknown.
If the view flag is 0 or X does not have the correct form, then this instruction fails with exception testX.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
Set the interpreter's trace flags. N is an OR of the following flags.
Modes for PRIM-TRACE | |
---|---|
1 | Trace each instruction as it is executed. |
2 | Show the environment at each instruction. |
4 | Trace input/output operations and a few extra things. |
8 | Trace searching of global definition tables. |
16 | Show the type stack with each instruction. |
32 | Show the trap set with each instruction. |
128 | Show control tree with each instruction. The tree is shown upwards from the current thread leaf. |
The trace flags are set to exactly the values indicated by N, regardless of their former values. Traces are printed to the trace file. See options for the interpreter for controlling the trace file.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
S* : List(Char) |
S is the name of the function to which this function will return when it is done. (Typically, this is also the name of the function that called this function. But if tail-recursion improvement has been used, that might not be the case.)
If this function is at the bottom of the call stack then S is "(nobody)"
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X | |
F* : SysOutfile |
Print a description of the internal representation of X onto file F. This is only useful for low-level debugging.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
F* : SysOutfile |
Show the environment of the current function, including nonlocal bindings, but excluding the static environment, on file F.
If the interpreter is being run in normal mode, bindings that have been marked as no longer needed will not be shown because they have been destroyed. If the interpreter is being run in debug mode (-t), all of the bindings should be available.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
F* : SysOutfile |
Dump the entire current configuration onto file F.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
X* : Boolean |
The interpreter can keep elementary profile information about how many instructions are performed by each function. If X is true then this instruction turn on profiling. If X is false then profiling is turned off.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
The interpreter keeps a soft limit on the depth of the call stack of any single thread. If the stack becomes deeper than this limit, the interpreter asks the user whether to continue. This instruction sets the stack limit to N.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
N is the interpreter's current soft limit on the depth of the call stack.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
N is the current depth of the call stack for the current thread. The call stack depth does not include the function that is running. It only includes those that are in the stack waiting for a return.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
The interpreter keeps a soft limit on the amount of data in garbage-collected memory (the heap). If the heap becomes deeper than this limit, the interpreter asks the user whether to continue. This instruction sets the heap limit to N kilobytes.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
N is the interpreter's current soft limit on the size of the heap, in kilobytes.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
The interpreter keeps a soft limit on the number of active threads. This instruction sets that limit to N.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
N is the interpreter's current soft limit on the number of threads.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
The interpreter keeps a soft limit on the depth of deferred evaluation (evaluating a promise while evaluating a promise while evaluating another promise, ...). This instruction sets the lazy evaluation depth to N.
Modify the data stack as follows.
Data stack | |
---|---|
before | after |
N* : Natural |
N is the interpreter's current soft limit on the depth of deferred evaluation.
Request a garbage collection now. You probably do not want to do this.
Data stack | |
---|---|
before | after |
() |
The garbage collector can move things around. This causes box ranks to change. Pop Xfrom the data stack. If X is true, then turn off compactification. If X is false then run compactification on. If you leave compactification off for a long time you will get serious fragmentation problems.
Data stack | |
---|---|
before | after |
X* : Boolean |
When indicating the number of environment entries needed, an index in the following table is used. Use an index that has an associated size that is at least as large as is needed.
Environment sizes | |
---|---|
Index | Maximum number of bindings |
0 | 0 |
1 | 4 |
2 | 8 |
3 | 16 |
4 | 32 |
5 | 64 |
6 | 128 |
7 | 255 |
Some exceptions, such as subscriptX, are simple values. Some have structure, so that they can supply more information. For example, domainX(s) has a string s associated with it. All of the standard exceptions that have structure are accompanied by strings.
The built-in exceptions are as follows. Each has a number indicated in the table, a brief description of its intended meaning and an indication of whether it is marked as trapped initially.
Standard exceptions | |||
---|---|---|---|
Number | Exception | Indication | Trap |
0 | endThreadX | A thread terminated itself | no |
1 | detachX | A thread detached itself | no |
2 | terminateX | A thread was terminated by another thread | no |
3 | testX | A test failed | no |
4 | conversionX | A conversion could not be done | no |
5 | sizeX | Size mismatch | yes |
6 | speciesX | There was a run-time species mismatch | no |
7 | notFoundX | A sought thing was not found | no |
8 | listExhaustedX | Attempt to read a value from an empty string | no |
9 | memoryX | Ran out of memory | yes |
10 | limitX | A compiled-in limit, such as path name length, was exceeded | yes |
11 | emptyBoxX | Attempt to get the content of an empty box | yes |
12 | emptyListX | Attempt to get the head or tail of an empty list | yes |
13 | bindUnknownX | Cannot bind an unknown | no |
14 | subscriptX | Subscript out of bounds | yes |
15 | domainX(s) | A function was given a parameter that it cannot handle | yes |
16 | noDefX(s) | A (name,species) pair has no definition in the global definition table | no |
17 | noCaseX | No case was available to handle a computation | yes |
18 | noMatchCaseX(s) | No case was found to handle a definition | no |
19 | infiniteLoopX | There was an infinite loop during lazy evaluation | yes |
20 | noFileX(s) | A file does not exist or has incorrect permission | no |
21 | fileTypeX(s) | A file has the wrong type | no |
22 | cannotSeekX | A seek could not be done on a file | yes |
23 | tooManyFilesX | Too many open files | yes |
24 | closedFileX | Attempt to write to a closed file | yes |
25 | interruptX | The interpreter was interrupted and the thread terminated | no |
26 | systemX(s) | An error occurred creating a process | no |
27 | networkX | A network error occurred | no |
28 | hostNameX(s) | Unknown host name | no |
Some of the instructions force evaluation of promises, and some do not. The following labels on parameters indicate evaluation. When they occur on an input to an instruction (in the before stack) they indicate evalation that the instruction will perform, if necessary. When these occur on the output of an instruction (in the after column) they indicate a level of evaluation that is guaranteed to have been done already.
It is important to distinguish partial evaluation from full evaluation. A list can be partially evaluated (indicated by *) by discovering that it has a given head and a given tail. The head and tail might themselves still be promises. If a list is fully evaluated, the entire list structure is determined, and each member of the list is evaluated.
For simple values (including untagged numbers, boxes, functions and SysOutfiles) there is no difference between partial evaluation and full evaluation. There are no parts to be left as promises. Full evaluation never evaluates the content of a box. It stops at the box itself. Species as values are always either unevaluated or fully evaluated. They can never have unevaluated parts. Exception sets are similarly always either unevaluated or fully evaluated.
Forcing notes | |
---|---|
Mark | Meaning |
* | Forces evaluation of a promise to the point where it is no longer a promise, but might not evaluate further promises that lie beneath the top level structure. |
** | Forces evaluation of promises until no more promises are found by going into pairs and lists. Does not attempt to evaluate the content of a box. |
*** | Evaluates list structure to as deep a level as necessary to compute its result, but does not evaluate list members. |
**** | Evaluation not determined. Might be lazy, and might evaluate structure needed for its result. |