.bp .ce 10 \f3Appendix A \(em The Parse Tree\fR .ce 0 .sp 1 .PP The parse tree is a collection of nodes, described below, rooted at a \f3proc\fR node. Nodes have a common format: the first field contains the node type, the second and third fields contain a line and column number relating the node to the source program, and the next zero to four fields contain node-dependent information. The line and column numbers are usually those of the first token or the primary token of the construct; for example, in \f3binop\fR nodes, they are the location of the operator; in \f3if\fR nodes, they are the location of the \fMif\fR token. .PP The following list of node types gives a brief description of the node and a list of the node-dependent fields and their uses. The fields are named .I val if they contain an integer value, .I str if they contain a pointer to a string, or .I tree if they contain a pointer to another node (a leaf or subtree). A digit between 0 and 3 is appended indicating its position in the node. .PP Seven of the nodes \(em \f3cset\fR, \f3id\fR, \f3int\fR, \f3op\fR, \f3real\fR, \f3res\fR, and \f3str\fR \(em are leaf nodes. These nodes, allocated and returned by the lexical analyzer, represent source program tokens. The remaining nodes contain one or more pointers to other nodes, either leaves or subtrees. .LP .nr Z \n(PD .nr PD \nZ .de X .IP \f3\\$1\fR 8n .. .de Y .nr PD 0 .IP \h'10n'\fI\\$1\fR \w'\fItree1'u+12n .. .sp \nZu .KS .X activat A transmission expression (\fIe1\fR @ \fIe2\fR). .Y tree0 The operator (an \f3op\fR node). .Y tree1 \fIe1\fR. .Y tree2 \fIe2\fR. .KE .sp \nZu .KS .X alt An alternation expression (\fIe1\fR | \fIe2\fR). .Y tree0 \fIe1\fR. .Y tree1 \fIe2\fR. .KE .sp \nZu .KS .X augop An augmented assignment expression (\fIe1\fR \(ci\fM:=\fR \fIe2\fR). .Y tree0 The operator. .Y tree1 \fIe1\fR. .Y tree2 \fIe1\fR. .KE .sp \nZu .KS .X bar A repeated alternation expression (\^\^|\fIe\fR). .Y tree0 \fIe\fR. .KE .sp \nZu .KS .X binop A binary operation (\fIe1\fR \(ci \fIe2\fR). .Y tree0 The operator. .Y tree1 \fIe1\fR. .Y tree2 \fIe2\fR. .KE .sp \nZu .KS .X break A \fMbreak\fR expression (\fMbreak [\fIe\fM]\fR). .Y tree0 \fIe\fR. .KE .sp \nZu .KS .X case A \fMcase\fR expression (\fMcase \fIe\fM of { \*(El }\fR). .Y tree0 \fIe\fR. .Y tree1 The list of case clauses. If there is only one case clause, this field points to the \f3ccls\fR node; if there are more, it points to a \f3clist\fR node. .KE .sp \nZu .KS .X ccls A case clause (\fIe1\fR : \fIe2\fR). .Y tree0 \fIe1\fR. For a \fMdefault\fR clause, this field points to a \f3res\fR node that contains the reserved word \fMdefault\fR. .Y tree1 \fIe2\fR. .KE .sp \nZu .KS .X clist A list of case clauses. The list is represented as a binary tree, with left branches pointing to case clauses and right branches pointing to a list of the remaining case clauses. The right branch of the last \f3clist\fR node points directly to a \f3ccls\fR node. .Y tree0 A case clause (pointer to a \f3ccls\fR node). .Y tree1 Pointer to another \f3clist\fR node, or to the last \f3ccls\fR node in the list. .KE .sp \nZu .KS .X conj A conjunction expression (\fIe1 \fM&\fI e2\fR). .Y tree0 \fIe1\fR. .Y tree1 \fIe2\fR. .KE .sp \nZu .KS .X create A \fMcreate\fR expression (\fMcreate\fI e\fR). .Y tree0 \fIe\fR. .KE .sp \nZu .KS .X cset A leaf node representing a cset literal. .Y str0 The string equivalent of the literal. .Y val1 The length of the string. .KE .sp \nZu .KS .X elist An expression list, as in a list construction or the argument list in a procedure call. An expression list, like a list of case clauses, is represented as a binary tree. .Y tree0 An expression. .Y tree1 Pointer to another \f3elist\fR node, or to the last expression in the list. .KE .sp \nZu .KS .X empty This node is used as a placeholder for missing expressions in control structures and expression lists. .KE .sp \nZu .KS .X field A field reference to a record (\fIe\fM . \fIident\fR). .Y tree0 \fIe\fR. .Y tree1 Pointer to an \f3id\fR node, containing the field name \fIident\fR. .KE .sp \nZu .KS .X id A leaf node representing an identifier. .Y str0 The name of the identifier. .KE .sp \nZu .KS .X if An \fMif\fR expression (\fMif\fI e1 \fMthen\fI e2 \fM[\fMelse\fI e3\fM]\fR). .Y tree0 \fIe1\fR. .Y tree1 \fIe2\fR. .Y tree2 \fIe3\fR. .KE .sp \nZu .KS .X int A leaf node representing an integer literal. .Y str0 The string representation of the literal. .KE .sp \nZu .KS .X invok A procedure call or mutual evaluation expression (\fIe\fM ( \fIargs\fM )\fR). .Y tree0 \fIe\fR. .Y tree1 The argument list \fIargs\fR. If there is one argument, this field points to the expression; if there are more, it points to an .I elist node. .KE .sp \nZu .KS .X key A keyword reference (\fM&\fI\^ident\fR). .Y val0 The index of the keyword \fIident\fR, defined in the file \fMtran/keyword.h\fR. .KE .sp \nZu .KS .X limit A limitation expression (\fIe1\fR \e \fIe2\fR). .Y tree0 \fIe1\fR. .Y tree1 \fIe2\fR. .KE .sp \nZu .X list A list (\^\fM[\fIe1\fR, \fIe2\fR, \*(El \fM]\fR). .Y tree0 The list of elements. If there is one element, this field points to the expression; if there are more, it points to an \f3elist\fR node. .KE .sp \nZu .KS .X loop A loop expression (\fIloop\fR \fIe1\fR [\fMdo\fI e2\fR]). .Y tree0 The style of loop. This field points to a \f3res\fR node, which identifies the reserved word that introduced the loop. .Y tree1 \fIe1\fR. .Y tree2 \fIe2\fR. .KE .sp \nZu .KS .X next A \fMnext\fR expression. .Y .sp -1 .KE .sp \nZu .KS .in 0 .X not A \fMnot\fR expression (\fNnot \fIe\fR). .Y tree0 \fIe\fR. .KE .sp \nZu .KS .X op A leaf node representing an operator. .Y val0 The token type of the operator. .KE .sp \nZu .KS .X proc A procedure. This node is always at the root of the parse tree. .Y tree0 The procedure name. This field points to an \f3id\fR node containing the name. .Y tree1 The \fMinitial\fR clause. .Y tree2 The procedure body. If there is one expression in the procedure body, this field points to it; if there are more, it points to an \f3elist\fR node. .Y tree3 A node containing the \fMend\fR token. This field is used to supply a line number for the implicit return at the end of a procedure. .KE .sp \nZu .KS .X real A leaf node representing a real number literal. .Y str0 The string representation of the literal. .KE .sp \nZu .KS .X res A leaf node representing a reserved word. .Y val0 The token type of the reserved word. .KE .sp \nZu .KS .X ret A \fMreturn\fR or \fMfail\fR expression. .Y tree0 The type of return. This field points to a \f3res\fR node, which contains the reserved word \fMreturn\fR or \fMfail\fR. .Y tree1 The expression following \fMreturn\fR, or a pointer to an \f3empty\fR node. .KE .sp \nZu .KS .X scan A scanning expression (\fIe1\fR \fM?\fR \fIe2\fR). .Y tree0 The operator. .Y tree1 \fIe1\fR. .Y tree2 \fIe2\fR. .KE .sp \nZu .KS .X sect A section expression (\fIe1\fR [\fIe2\fR : \fIe3\fR]). .Y tree0 \fIe1\fR. .Y tree1 \fIe2\fR. .Y tree2 \fIe3\fR. .KE .sp \nZu .KS .X slist A list of expressions separated by semicolons, as in a procedure body (a statement list). This list, like expression lists and case lists, is represented as a binary tree. .Y tree0 An expression in the list. .Y tree1 A pointer to another \f3slist\fR node, or to the last expression in the list. .KE .sp \nZu .KS .X str A leaf node representing a string literal. .Y str0 The string value of the literal. .Y val1 The length of the string, necessary because the string may contain the \s-2ASCII\s+2 .I null character, which would otherwise terminate the string. .KE .sp \nZu .KS .X susp A \fMsuspend\fR expression (\fMsuspend\fR [\fIe\fR]). .Y tree0 \fIe\fR. .KE .sp \nZu .KS .X toby A \fMto-by\fR expression (\fIe1\fM to \fIe2\fM by \fIe3\fR). .Y tree0 \fIe1\fR. .Y tree1 \fIe2\fR. .Y tree2 \fIe3\fR. .KE .sp \nZu .KS .X to A \fMto\fR expression (\fIe1\fM to \fIe2\fR). .Y tree0 \fIe1\fR. .Y tree1 \fIe2\fR. .KE .sp \nZu .KS .X unop A unary operation (\(ci \fIe\fR). .Y tree0 The operator. .Y tree1 \fIe\fR. .KE .nr PD \nZ .bp .ce 10 \f3Appendix B \(em Icon Grammar\fR .ce 0 .sp 1 .PP The following grammar describes the Icon language. Reserved words and operators are shown in a sans-serif type face; nonterminals are in italics. The nonterminals .I ident , .I literal , .I strliteral , and .I empty are left undefined in the syntax. .sp 2 .ta 1.5iR 1.5i+1mL .ft I .tr |\(br .tr -\- .tr +\(pl .tr /\(sl .tr *\(** .ss 9 .de X \t\\$1\h'1m'\(->\t\\$2 .br .. .de Y .if n .sp 1 .if t .sp .5 .. .X program "decls" .Y .ne 2 .X decls "empty" .X "" "decls decl" .Y .ne 3 .X decl "record" .X "" "proc" .X "" "global" .X "" "link" .Y .X link "\fMlink\fI lnklist" .Y .ne 2 .X lnklist "lnkfile" .X "" "lnklist , lnkfile" .Y .X lnkfile "ident" .X "" "strliteral" .Y .ne 2 .X global "\fMglobal\fI idlist" .Y .X record "\fMrecord\fI ident \fM(\fI arglist \fM)\fI" .Y .X proc "prochead \fM;\fI locals initial procbody \fMend\fI" .Y .X prochead "\fMprocedure\fI ident \fM(\fI arglist \fM)\fI" .Y .ne 2 .X arglist "empty" .X "" "idlist" .Y .ne 2 .X idlist "ident" .X "" "idlist \fM,\fI ident" .Y .ne 2 .X locals "empty" .X "" "locals retention idlist \fM;\fI" .Y .ne 2 .X retention "\fMlocal\fI" .X "" "\fMstatic\fI" .X "" "\fMdynamic\fI" .Y .ne 2 .X initial "empty" .X "" "\fMinitial\fI expr \fM;\fI" .Y .ne 2 .X procbody "empty" .X "" "nexpr \fM;\fI procbody" .Y .ne 2 .X nexpr "empty" .X "" "expr" .Y .ne 2 .X expr "expr1a" .X "" "expr \fM&\fI expr1a" .Y .ne 2 .X expr1a "expr1" .X "" "expr1a \fM?\fI expr1" .Y .ne 5 .X expr1 "expr2" .X "" "expr2 op1 expr1" .X "" "expr2 op1a expr1" .X "" "expr2 \fM?:=\fI expr1" .X "" "expr2 \fM&:=\fI expr1" .X "" "expr2 \fM@:=\fI expr1" .Y .ne 6 .X op1 "\fM:= | :=: | <- | <->\fI" .Y .if n .X op1a "\fM+:= | -:= | *:= | /:= | %:= | ^:=\fI" .if n .X "" "\fM++:= | --:= | **:= | \(or\(or:= | \(or\(or\(or:=\fI" .if t .X op1a "\fM+:= | -:= | *:= | /:= | %:= | ^:= | ++:= | \ --:= | **:= | \(or\(or:= | \(or\(or\(or:=\fI" .X "" "\fM<:= | <=:= | =:= | >=:= | >:= | ~=:= \fM" .X "" "\fM<<:= | <<=:= | ==:= | >>=:= | >>:= | ~==:=\fI" .X "" "\fM===:= | ~===:=\fI" .Y .ne 3 .X expr2 "expr3" .X "" "expr2 \fMto\fI expr3" .X "" "expr2 \fMto\fI expr3 \fMby\fI expr3" .Y .ne 2 .X expr3 "expr4" .X "" "expr4 \(or expr3" .Y .ne 2 .X expr4 "expr5" .X "" "expr4 op4 expr5" .Y .ne 3 .X op4 "\fM< | <= | = | >= | > | ~=\fI" .X "" "\fM<< | <<= | == | >>= | >> | ~==\fI" .X "" "\fM=== | ~===\fI" .Y .ne 2 .X expr5 "expr6" .X "" "expr5 op5 expr6" .Y .ne 2 .X op5 "\fM\(or\(or | \(or\(or\(or\fI" .Y .ne 2 .X expr6 "expr7" .X "" "expr6 op6 expr7" .Y .X op6 "\fM+ | - | ++ | --\fI" .Y .ne 2 .X expr7 "expr8" .X "" "expr7 op7 expr8" .Y .X op7 "\fM* | / | % | **\fI" .Y .ne 2 .X expr8 "expr9" .X "" "expr9 ^ expr8" .Y .ne 2 .X expr9 "expr10" .X "" "expr9 \fM\e\fI expr10" .X "" "expr9 \fM @ \fIexpr10" .Y .ne 5 .X expr10 "expr11" .X "" "\fMnot\fI expr10" .X "" "\fM@\fI expr10" .X "" "\(or expr10" .X "" "op10 expr10" .Y .X op10 "\fM. | ! | + | - | ~ | = | * | / | \e | ? | ^\fI" .Y .ne 20 .X expr11 "ident" .X "" "literal" .X "" "\fM&\fI ident" .X "" "expr11 \fM.\fI ident" .X "" "expr11 \fM[\fI nexpr \fM]\fI" .X "" "expr11 \fM(\fI exprlist \fM)\fI" .X "" "expr11 \fM{\fR exprlist \fM}\fI" .X "" "\fM[\fI exprlist \fM]\fI" .X "" "\fM(\fI exprlist \fM)\fI" .X "" "{ compound }" .X "" "while" .X "" "until" .X "" "every" .X "" "repeat" .X "" "\fMnext\fI" .X "" "\fMbreak\fI nexpr" .X "" "\fMcreate\fI expr" .X "" "if" .X "" "case" .X "" "return" .X "" "section" .Y .ne 2 .X while "\fMwhile\fI expr" .X "" "\fMwhile\fI expr \fMdo\fI expr" .Y .ne 2 .X until "\fMuntil\fI expr" .X "" "\fMuntil\fI expr \fMdo\fI expr" .Y .ne 2 .X every "\fMevery\fI expr" .X "" "\fMevery\fI expr \fMdo\fI expr" .Y .X repeat "\fMrepeat\fI expr" .Y .ne 2 .X if "\fMif\fI expr \fMthen\fI expr" .X "" "\fMif\fI expr \fMthen\fI expr \fMelse\fI expr" .Y .X case "\fMcase\fI expr \fMof\fI { caselist }" .Y .ne 2 .X caselist "cclause" .X "" "caselist \fM;\fI cclause" .Y .ne 2 .X cclause "\fMdefault\fM :\fI expr" .X "" "expr \fM:\fI expr" .Y .ne 3 .X return "\fMfail\fI" .X "" "\fMreturn\fI nexpr" .X "" "\fMsuspend\fI nexpr" .Y .X section "expr11 \fM[\fI expr sectop expr \fM]\fI" .Y .X sectop "\fM: | +: | -:\fI" .Y .ne 2 .X exprlist "nexpr" .X "" "exprlist \fM,\fI nexpr" .Y .ne 2 .X compound "nexpr" .X "" "nexpr \fM;\fI compound" .ss 4 .tr || .tr -- .tr ++ .tr // .tr ** .uf I .ft R .bp .de Op .if \\n(.$=2 .IP \h'3n'\f3\\$1\h'2n'\fI\\$2\fR 6n .if \\n(.$=1 .IP \h'3n'\f3\\$1\fR 6n .br .. .ce 10 \f3Appendix C \(em Ucode\fR .ce 0 .sp 1 .PP The intermediate ucode generated by the Icon translator resembles a stack-oriented assembly language. A ucode program is a sequence of labels and instructions. A label marks a location in the program to which other instructions may transfer control. Labels are of the form \*(oq\f3lab L\fIn\fR\*(cq, where .I n is a decimal number. A ucode instruction either describes an imperative operation or communicates information to the Icon linker. Instructions consist of an opcode followed by zero or more arguments. Arguments can be decimal or octal integers, names, or label references. .PP The intermediate language operates exclusively on the stack. There are several kinds of objects that can appear on the stack: descriptors, which represent Icon values and variables; procedure frame markers, which mark the beginning of a new procedure frame; expression frame markers, which delimit expression instances; and generator frame markers, which mark inactive generators. For more details about the stack, refer to Section 3.2. .PP The opcodes and their arguments are described in three groups below. The global symbol table file has a similar format. The opcodes used there are described in the fourth group. .sp 1 .SH Imperative Instructions .PP The instructions below, together with the operators described in the next section, represent run-time actions for which code is executed. .KS .Op bscan Save the values of \fM&subject\fR and \fM&pos\fR on the stack and establish values for them. This operation is reversible. .KE .KS .Op ccase Duplicate the value on the stack just below the current expression frame. Used in \fMcase\fR expressions. .KE .KS .Op chfail lab Change the failure label for the current expression frame to .I lab . Used for repeated evaluation. .KE .KS .Op coact Switch co-expression evaluation. Create a procedure frame on the current co-expression stack. Transfer the result from old stack to new stack, dereferencing if necessary. Set the activator field in new stack block to point to old co-expression stack block. Return from procedure frame on new co-expression stack. .KE .KS .Op cofail Fail from current co-expression to activating co-expression. Create a procedure frame on current co-expression stack. Fail from procedure frame on activator's co-expression stack. .KE .KS .Op coret Switch evaluation to activating co-expression. Create a procedure frame on current co-expression stack. Transfer the result from old stack to activator's stack, dereferencing it if the result is on the old stack. Return from the procedure frame on new co-expression stack. .KE .KS .Op create Create a co-expression. Allocate co-expression stack and heap blocks. Copy the arguments and locals variables from the current procedure frame into the heap block. Create a procedure frame in the new co-expression stack using the arguments and other locals from current procedure frame. Create a procedure frame for dummy call to \fMcoact\fR on the new co-expression stack. Push a descriptor representing the new co-expression onto current co-expression stack. .KE .KS .Op cset n Push a descriptor representing the cset literal at constant table location .I n onto the stack. .KE .KS .Op dup Push a descriptor representing the null value onto the stack, and then duplicate the value that was the previous top of the stack. Used in most augmented assignments. .KE .KS .Op efail Signal failure in the current expression. If there are any inactive generators, reactivate the most recent one. If there are none, branch to the failure label for the current expression frame. If the failure label is null, exit the current expression frame, and signal failure in the enclosing one. .KE .KS .Op eret Return a value from an expression. Save the value on top of the stack, exit the current expression frame, and push the value onto the stack as part of the enclosing expression frame. .KE .KS .Op escan Restore \fM&subject\fR and \fM&pos\fR from the stack. This operation is reversible. .KE .KS .Op esusp Suspend a value from an expression. The value on the top of the stack is saved, and a generator frame hiding the current expression frame is created. The surrounding expression frame is duplicated, and the value is pushed onto the stack as part of that expression frame. When reactivated, \f3esusp\fR in turn reactivates any inactive generators in the suspended expression. .KE .KS .Op field name Access the field .I name of the record on the top of the stack. .KE .KS .Op file name Set the file name to .I name for use in error messages and tracing. Used at the beginning of each procedure. .KE .KS .Op goto lab Transfer control to the instruction following label .I lab . .KE .KS .Op incres Increment result count field in current co-expression stack block. .KE .KS .Op init? lab If the initialization statement for the current procedure has already been executed once, go to .I lab . .KE .KS .Op int n Push a descriptor representing the integer literal at constant table location .I n onto the stack. .KE .KS .Op invoke n Invoke a procedure or create a record. The number of arguments or fields on the stack is given by .I n . The procedure (which may be a record constructor) is on the stack, just beyond the arguments. After invocation, the arguments are popped from the stack, and the returned value is pushed (see \f3pret\fR). .KE .KS .Op keywd n Push a descriptor representing a value or trapped variable representing keyword .I n onto the stack. (See \fMtran/keyword.h\fR for keyword numbers.) .KE .KS .Op limit Check the value on the top of the stack for a legal limitation value. If the value is zero, failure is signalled in the current expression (see \f3efail\fR). .KE .KS .Op line n Set the line number to .I n for use in error messages and tracing. .KE .KS .Op llist n Create a list of .I n values. The values are popped from the stack and the created list is pushed back onto the stack. .KE .KS .Op lsusp Decrement the limitation counter for the current expression frame. If the counter becomes zero, then return a value from the current expression frame (see \f3eret\fR); otherwise, suspend a value from the current expression frame (see \f3esusp\fR). .KE .KS .Op mark lab Save the current expression and generator frame pointers on the stack, then create a new expression frame, with failure label .I lab . Control is transferred to .I lab if failure occurs in the expression frame and there are no suspended generators to reactivate (see \f3efail\fR). The failure label \fML0\fR indicates that control is to be transferred to the failure label in the enclosing expression. .KE .KS .Op pfail Return from the current procedure, and signal failure (see \f3efail\fR). .KE .KS .Op pnull Push a descriptor representing the null value onto the stack. .KE .KS .Op pop Pop the top element off of the stack. .KE .KS .Op pret Return from the current procedure with the result that is on top of the stack. .KE .KS .Op psusp Suspend from the current procedure with the result that is on top of the stack. .KE .KS .Op push1 Push a descriptor representing the integer 1 onto the stack. .KE .KS .Op pushn1 Push a descriptor representing the integer \-1 onto the stack. This is used as default in mutual goal-directed evaluation. .KE .KS .Op real n Push a descriptor representing the real literal at constant table location .I n onto the stack. .KE .KS .Op refresh Allocate space for a new co-expression stack. Create a procedure frame in new co-expression stack using arguments and other locals from entry block for co-expression operand. Create a procedure frame for dummy call to \fMcoact\fR on new co-expression stack. Push a descriptor representing the new co-expression onto current co-expression stack. .KE .KS .Op sdup Duplicate the descriptor on the top of the stack. Used in assignment augmented with string scanning. .KE .KS .Op str n Push a descriptor representing the string literal at constant table location .I n onto the stack. .KE .KS .Op unmark n Exit from .I n expression frames. No value is pushed onto the stack in their place. .KE .KS .Op var n Push the descriptor for the variable at location .I n in the local symbol table onto the stack. .KE .sp 1 .SH Operators .PP The instructions below perform the functions corresponding to the indicated Icon operator. The operands are evaluated and pushed onto the stack from left to right, so that the topmost element of the stack is the rightmost operand. The operands are popped before the result of the operation is pushed onto the stack. All operations dereference their operands as necessary, but only after all operands have been evaluated and pushed onto the stack. All operations attempt to convert their operands to an appropriate type. If this implicit conversion fails, an error is issued. Relational tests fail if the specified condition is not met; the result of a successful comparison is the value of the right-hand operand. Arithmetic operations cause an error to be issued if the result overflows or underflows. If an operation cannot be performed for some other reason, it fails. .LP .ta 0.5i 1.5i 3.5i 4.5i .de X \t\f3\\$1\fM\t\\$2\t\f3\\$3\fM\t\\$4\fR .br .. .ne 21 .tr |\(or .tr +\(pl .tr -\- .tr /\(sl .tr *\(** .ss 9 .X asgn "x := y" null "/x" .X bang "!x" number "+x" .X cat "x || y" numeq "x = y" .X compl "~x" numge "x >= y" .X diff "x -- y" numgt "x > y" .X div "x / y" numle "x <= y" .X eqv "x === y" numlt "x < y" .X inter "x ** y" numne "x ~= y" .X lconcat "x ||| y" plus "x + y" .X lexeq "x == y" power "x ^ y" .X lexge "x >>= y" random "?x" .X lexgt "x >> y" rasgn "x <- y" .X lexle "x <<= y" rswap "x <-> y" .X lexlt "x << y" sect "x\^[\^y:z]" .X lexne "x ~== y" size "*x" .X minus "x - y" subsc "x\^[\^y]" .X mod "x % y" swap "x :=: y" .X mult "x * y" tabmat "=x" .X neg "-x" toby "x to y by z" .X neqv "x ~=== y" unioncs "x ++ y" .X nonnull "\ex" value ".x" .tr || .tr ++ .tr -- .tr // .tr ** .ss 4 .sp 1 .SH Non-Imperative Instructions .PP The following instructions generate no executable code. Instead, they communicate various information to the linker each procedure and its symbol table. An Icon procedure is translated into a sequence of ucode instructions beginning with a \f3proc\fR instruction, followed by a sequence of \f3local\fR instructions, a sequence of \f3con\fR instructions, a \f3declend\fR instruction, then the imperative instructions describing the procedure body. An \f3end\fR instruction terminates the procedure. .KS .Op proc name Begin a new procedure with the indicated name. The local and constant tables are initialized. The procedure block is not generated at this time, since the local identifiers have not yet been declared. .KE .KS .Op local n,flags,name Enter .I name into the current procedure's local symbol table at location .I n . The symbol's .I flags indicate information about the symbol, its scope, and its retention. All identifiers referred to in a procedure appear in the local symbol table. If an identifier is undeclared, its scope is determined by consulting the global symbol table and a list of functions. .KE .KS .Op con n,flags,value Enter .I value into the current procedure's constant table at location .I n in the table. The type of the constant (integer, real, or string) is indicated by .I flags . For integer and real literals, .I value is an 11-digit octal number; for string and cset literals, it is a comma-separated list of 3-digit octal numbers, each representing one byte in the string. .KE .KS .Op declend Signal the end of the procedure prologue. The procedure block is generated at this point. .KE .KS .Op end Signal the end of a procedure. .KE .sp 1 .SH Global Symbol Table Instructions .PP A single global symbol table file is output during each translation. Record declarations appear first in the file; they are output as they are encountered in the Icon source program. The first instruction following the record declarations is \f3impl\fR, which may be followed by a \f3trace\fR instruction, then by the global declarations. The global declarations are output at the end of translation. .KS .Op record name,n Declare a record with the indicated name and .I n fields. One line for each field follows this line, each containing the field number and name. .KE .KS .Op impl scope Declare the implicit scope as indicated. .I Scope can be either \f3local\fR or \f3error\fR. If the implicit scope is \f3error\fR, undeclared identifiers are flagged as warnings during linking; otherwise, they are made local variables. The implicit scope is \f3error\fR if the \f3\-u\fR option was given on the translator command line, otherwise it is \f3local\fR. .KE .KS .Op trace Enable run-time tracing. This instruction is present if the \f3\-t\fR option was given on the translator command line, and causes the keyword \fM&trace\fR to be initialized to \-1. .KE .KS .Op global n Begin the global symbol table. There are .I n global declarations following, one per line. Each global declaration contains a sequence number, the flags, the identifier name, and the number of formal parameters (for procedures) or fields (for records). .KE .KS .Op link name Search each directory named in the \fIIPATH\fR environment variable for a file named \fIname.u1\fR. If the file is located, it is added to the list of files to link. .KE .bp .if \nv .ss 9 .ce 10 \f3Appendix D \(em Data Representations\fR .ce 0 .sp 1 .SH Descriptor Formats .PP The figures below depict each of the six descriptor types mentioned in Section 3.1. Each descriptor is two words long; the first word is shown on top of the second. .sp 1 .TS center tab(:) ; L0w(2.2i) | L0w(2n)e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e | LI | L S S S S S S S S S S S S S S C | L | L S S S S S S S S S S S S S S S | L | L S S S S S S S S S S S S S S C | L | L S S S S S S S S S S S S S S S | . :_ Null::0 :_ ::0 :_ .TE .sp 1 .TS center tab(:) ; L0w(2.2i) | L0w(2n)e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e | LI | C | CI S S S S S S S S S S S S S S | L | L S S S S S S S S S S S S S S S | L | CI S S S S S S S S S S S S S S S | L | L S S S S S S S S S S S S S S S | . :_ String Qualifier:0:length :_ :address of string :_ .TE .sp 1 .TS center tab(:) ; L0w(2.2i) | L0w(2n)e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e | LI | C | C | CI S S S S S S S | CI S S S S S | L | L S S S S S S S S S S S S S S S | LI | CI S S S S S S S S S S S S S S S | L | L S S S S S S S S S S S S S S S | . :_ Integer:1:0:flags:type = \fR1 :_ :integer :_ .TE .sp 1 .TS center tab(:) ; L0w(2.2i) | L0w(2n)e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e | LI | C | C | CI S S S S S S S | CI S S S S S | L | L S S S S S S S S S S S S S S S | LI | CI S S S S S S S S S S S S S S S | L | L S S S S S S S S S S S S S S S | . :_ Value:1:0:flags:type \(>= \fR2 :_ :address of data block :_ .TE .sp 1 .TS center tab(:) ; L0w(2.2i) | L0w(2n)e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e | LI | C | C | C | CI S S S S S S S S S S S S | L | L S S S S S S S S S S S S S S S | LI | CI S S S S S S S S S S S S S S S | L | L S S S S S S S S S S S S S S S | . :_ Variable:1:1:0:offset :_ :address of descriptor :_ .TE .sp 1 .TS center tab(:) ; L0w(2.2i) | L0w(2n)e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e S0e | LI | C | C | C | CI S S S S S S | CI S S S S S | L | L S S S S S S S S S S S S S S S | LI | CI S S S S S S S S S S S S S S S | L | L S S S S S S S S S S S S S S S | . :_ Trapped Variable:1:1:1:flags:type :_ :address of data block :_ .TE \fINotes:\fR The offset in a variable descriptor is the number of words from the top of the block in which the descriptor that is pointed to occurs. The second word in the descriptors for the trapped variables for \fM&pos\fR, \fM&random\fR, and \fM&trace\fR contain addresses of locations in statically allocated data. .bp .SH Data Block Formats .PP The data blocks used by the Icon system are pictured below. The data type code, shown as both a mnemonic and an integer, is always the first word of the block and has the same value as the type code in the .I value or .I "trapped variable" descriptor that refers to it. All .I name fields in the data blocks are .I "string qualifier" descriptors, and all .I pointers in the data blocks are .I "variable" descriptors. .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Long Integer Block:T_LONGINT = 2 :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::32-bit integer :_::_ :_ .TE \fINote:\fR Long integers apply only when \fMsizeof(int) != sizeof(long)\fR .sp .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Real Block:T_REAL = 3 :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L L ^ L L | L ^ L | L | L ^ L | L | L ^ L | L | L S S | . ::double-precision real :_::_ :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Cset Block:T_CSET = 4 :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L ^ L | L | L ^ L | L L ^ L L | L ^ L | L | L ^ L | L | L ^ L | L | L ^ L | L | L ^ L | L | L S S | . ::256-bit character set :_::_ :_::_ :_::_ :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ File Block:T_FILE = 5 :_ .T& L | CI S S | . :\s-2UNIX\s+2 file descriptor :_ :file status :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::file name :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Procedure Block:T_PROCEDURE = 6 :_ .T& L | CI S S | . :size of this data block :_ :entry point address :_ :number of arguments :_ :number of dynamic locals :_ :number of static locals :_ :index of first static local :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::procedure name :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::name of first identifier :_::_ :_ .T& L | L CB L | L L CB L L | L CB L | L | L S S | . ::. ::. ::. :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::name of last identifier :_::_ :_ .TE \fINotes:\fR Identifiers include arguments and locals. Similar blocks are used for built-in functions; in this case the word for the number of dynamic locals contains \-1. For functions, there are no argument names. Functions like \fMwrite\fR, which have an arbitrary number of argument, are indicated by the value \-1 in place of the number of arguments. Record constructors are distinguished from other functions by the value \-2 in place of the number of dynamic locals. Each record declaration is distinguished by a unique record identification number, which appears in place of the number of static locals. .sp .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ List Header Block:T_LIST = 7 :_ .T& L | CI S S | . :current size of list :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::pointer to first list block :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::pointer to last list block :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ List Element Block:T_LELEM = 11 :_ .T& L | CI S S | . :size of this data block :_ :number of slots in this block :_ :index of first slot used :_ :number of slots used :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::pointer to previous list block :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::pointer to next list block :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::first slot :_::_ :_ .T& L | L CB L | L L CB L L | L CB L | L | L S S | . ::. ::. ::. :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::last slot :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Table Header Block:T_TABLE = 8 :_ .T& L | CI S S | . :current table size :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::default value :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::first hash bucket :_::_ :_ .T& L | L CB L | L L CB L L | L CB L | L | L S S | . ::. ::. ::. :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::last hash bucket :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Table Element Block:T_TELEM = 10 :_ .T& L | CI S S | . :hash number :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::pointer to next element :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::entry value :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::assigned value :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Set Header Block:T_SET = 20 :_ .T& L | CI S S | . :current set size :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::first hash bucket :_::_ :_ .T& L | L CB L | L L CB L L | L CB L | L | L S S | . ::. ::. ::. :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::last hash bucket :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Set Element Block:T_SELEM = 21 :_ .T& L | CI S S | . :hash number :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::member value :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::pointer to next member :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Record Block:T_RECORD = 9 :_ .T& L | CI S S | . :size of this data block :_ :pointer to record constructor :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::first field of record :_::_ :_ .T& L | L CB L | L L CB L L | L CB L | L | L S S | . ::. ::. ::. :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::last field of record :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Co-Expression Stack Block:T_ESTACK = 18 :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::most recent activator :_::_ :_ .T& L | CI S S | . :stack base :_ :stack pointer :_ :address pointer :_ :Icon/C boundary :_ :number of results produced :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::pointer to refresh block :_::_ :_ .T& L | L CB L | L L CB L L | L CB L | L | L S S | . ::. ::. ::. :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Co-Expression Heap Block:T_EBLOCK = 19 :_ .T& L | CI S S | . :size of this data block :_ :entry point address :_ :number of arguments :_ :number of locals :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::procedure :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::value of first identifier :_::_ :_ .T& L | L CB L | L L CB L L | L CB L | L | L S S | . ::. ::. ::. :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::value of last identifier :_::_ :_ .TE \fINote:\fR Identifiers include arguments and locals. .sp .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Substring Trapped Variable:T_TVSUBS = 12 :_ .T& L | CI S S | . :length of substring :_ :relative position of substring :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::variable containing substring :_::_ :_ .TE .KE .sp 1 .KS .TS center tab(:) ; L0w(2.2i) | L0w(2n) S0w(28n) S0w(2n) | LI | C S S | . :_ Table Element Trapped Variable:T_TVTBL = 14 :_ .T& L | CI S S | . :hash number :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::pointer to table :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . ::entry value :_::_ :_ .T& L | L CI L | L | L ^ L | L | L ^ L | L | L S S | . :: :_::_ :_ .TE \fINote\fR: The last descriptor in a table element trapped variable is not used until the element is inserted in the table, at which time the table element trapped variables is converted into a table element block. .KE .bp .ce 10 \f3Appendix E \(em Stack Frame Formats\fR .ce 0 .sp 1 .PP Stack frame formats depend on computer architecture and the C compiler that is used. Consequently, stack frame formats are specific to a particular implementation. This appendix gives the UNIX PDP-11 and VAX-11 stack frame formats. See [3] for a detailed description of the design of stack frame formats. .PP On the PDP-11 and VAX-11 stacks start in high memory and grow downward. On these computers, a push causes the stack pointer to decrease and a pop causes the stack pointer to increase, while ``top of the stack'' refers to the \fIlowest\fR memory location that is logically contained in the stack. The diagrams that follow are arranged accordingly. .PP There are three kinds of stack frames: \fIprocedure frames\fR, \fIexpression frames\fR, and \fIgenerator frames\fR. For each kind of frame, a .I "frame pointer" points to the most recent .I "frame marker" , which marks one end of the frame. These frame pointers are referred to as \fIpfp\fR, \fIefp\fR, and \fIgfp\fR, respectively. Each frame marker contains a pointer to the next most recent marker of the same kind. .sp 2 .ce \f3PDP-11 Stack Frame Formats\fR .sp 2 .PP On the PDP-11, \fIpfp\fR, \fIefp\fR, and \fIgfp\fR are in registers \fIr5\fR, \fIr4\fR, and \fIr3\fR, respectively, whenever an Icon procedure is active. In the interpreter implementation, .I r2 contains the interpreter's program counter (\fIipc\fR), which points to the next icode operation to be done. When a C procedure is active, only the procedure frame pointer is kept in a register; registers .I r2\-r4 are used for local variables by C procedures. .sp 1 .SH Procedure Frames on the PDP-11 .PP Icon procedure frames are augmented C procedure frames. A procedure frame contains a procedure's arguments, local variables, and temporary storage for incomplete computations. When an active procedure invokes another procedure, a new procedure frame is created for the new procedure, which then becomes active. As such, the new procedure represents an incomplete computation in the calling procedure, so the new procedure frame is .Q nested within the old one. The .I "procedure marker" is placed on the stack between the arguments and local variables. The format of the procedure frame is shown below. The locations are relative to \fIpfp\fR. .DS .ta 1iR 1.6i arguments 4 number of arguments 2 return address \fIpfp\fR \(-> 0 previous \fIpfp\fR \-2 previous \fIefp\fR \-4 previous \fIgfp\fR \-6 previous \fIipc\fR \-8 previous source program line number \-10 previous source program file name .DE Expression and generator frames are always contained wholly within a procedure frame, and their respective frame pointers are cleared to zero after being saved in the procedure marker. .PP The first argument to a procedure is located at 6(\fIpfp\fR), the second at 10(\fIpfp\fR), and so on. The first local variable is located at \-14(\fIpfp\fR), the second at \-18(\fIpfp\fR), and so on. .PP Procedure markers created for functions and operators do not contain the source program line number or file name, since functions and operators do not change it. Because they are C procedures, their local variables are not descriptors and are subject to C language conventions, but everything above the marker (higher addresses) is subject to Icon language conventions. The location of the procedure marker for functions and operators is considered the .I boundary , mentioned in Section 3.2. .sp 1 .SH Expression Frames on the PDP-11 .PP An expression frame limits the scope of backtracking. No inactive generator outside the current expression frame may be reactivated until evaluation of the current expression is complete. The format of an expression marker is shown below; locations are relative to \fIefp\fR. .DS .ta 1iR 1.6i \fIefp\fR \(-> 0 previous \fIefp\fR \-2 previous \fIgfp\fR \-4 failure label for expression frame .DE When an expression frame is created, the generator frame pointer is cleared after being saved in the expression marker, to indicate that there are no inactive generators that may be reactivated while the new expression frame is current. An expression frame extends from its expression marker to the top of the stack. Expression frames are not disjoint; new frames are always nested within older ones. .PP When failure occurs within an expression and there are no inactive generators to reactivate, the expression frame is exited, and control is transferred to the failure label. If the failure label is null, however, another failure occurs within the new expression frame, and the logic is the same. .PP For limited expressions, the limitation counter is contained in an Icon integer just above the expression marker at 2(\fIefp\fR). This counter is decremented each time the expression suspends a result. .SH Generator Frames on the PDP-11 .PP Generator frames are augmented procedure frames. A generator frame preserves the state of execution of a inactive generator. When a suspending procedure calls \fMpsusp\fR, a generator marker is placed on the stack to mark the point of suspension, and the most recent expression frame .I outside the suspending procedure frame (the expression frame that was current just prior to invocation of the suspending procedure) is then duplicated and pushed onto the stack. The suspending procedure then returns, so that the expression frame that was duplicated is current. Thus, the generator frame is contained within the expression frame, and .Q hides the inactive generator. The format of the generator marker is shown in the following table; locations are shown relative to \fIgfp\fR. .DS .ta 1iR 1.6i 10 reactivation address 8 previous \fIpfp\fR 6 previous \fIefp\fR 4 previous \fIgfp\fR 2 previous \fIipc\fR \fIgfp\fR \(-> 0 previous boundary address \-2 previous value of \fM&level\fR \-4 previous source program line number \-6 previous source program file name .DE The last five words of the generator marker are actually part of a procedure marker, created by the call to \fMpsusp\fR. Thus, the reactivation address is just the return address for \fMpsusp\fR. .PP When a function or operator suspends, there is a boundary that becomes hidden. This boundary address needs to be restored upon reactivation. It is also important to the garbage collector, since the portion of a generator frame between the hidden boundary and the generator marker does not have the well-defined structure that is required. .sp 2 .ce \f3VAX-11 Stack Frame Formats\fR .sp 2 .PP The C frames on the VAX are variable in size and \fIap\fR is used to facilitate access to the arguments. .SH Procedure Frames on the VAX-11 .PP On the VAX-11, there is a program counter (\fIpc\fR), a stack pointer (\fIsp\fR), a frame pointer (\fIfp\fR), and an argument pointer (\fIap\fR). These pointers are registers \fIr15\fR, \fIr14\fR, \fIr13\fR, and \fIr12\fR, respectively. Icon uses \fIfp\fR for \fIpfp\fR, \fIr11\fR for \fIefp\fR, and \fIr10\fR for \fIgfp\fR. .PP The procedure frame for the VAX-11 is: .DS .ta 1iR 1.6i arguments 4 number of arguments \fIap\fR \(-> 0 number of words in the argument list \-4 previous \fIefp\fR \-8 previous \fIgfp\fR .\^.\^. 16 previous \fIpc\fR 12 previous \fIfp\fR 8 previous \fIap\fR 4 program status word and register mask \fIfp\fR \(-> 0 0 (condition handler status) \-4 previous source program line number \-8 previous source program file name local variables .DE The first argument is at 8(\fIap\fR), the second argument is at 16(\fIap\fR), and so on. The first local variable is at \-16(\fIfp\fR)\fR, the second local variable is at \-24(\fIfp\fR), and so on. .SH Expression Frames on the VAX-11 .PP The expression frame marker for the VAX-11 is: .DS .ta 1iR 1.6i \fIefp\fR \(-> 0 previous \fIefp\fR \-4 previous \fIgfp\fR \-8 failure label for expression frame .DE .SH Generator Frames on the VAX-11 .PP The generator frame marker for the VAX-11 is: .DS .ta 1iR 1.6i previous \fIefp\fR previous \fIgfp\fR .\^.\^. last saved register 20 reactivation address 16 previous \fIfp\fR 12 previous \fIap\fR 8 program status word and register mask 4 0 (condition handler address) \fIgfp\fR \(-> 0 previous boundary address \-4 previous value of \fM&level\fR \-8 previous source program line number \-12 previous source program file name .DE .bp .ce 10 \f3Appendix F \(em Sample Functions\fR .ce 0 .sp 1 .PP The following routines are examples of the source code for Icon functions. As indicated in the report, each routine consists of a C procedure that performs the indicated function and a procedure block linking the C procedure with the Icon procedure invocation mechanism. .PP The first example is the code for the routine \fMwrite\fR, as supplied with the Icon distribution, and is included to show how a routine is written to handle a variable number of arguments. .Ds .ta 3.5i #include "../h/rt.h" /* * write(a,\*bb,\*b...) - write arguments. */ Xwrite(nargs) int nargs; { register int n; char sbuf\^[MAXSTRING]; struct descrip arg; FILE *f; f = stdout; arg = nullstr; for (n = 1; n \*(<= nargs; n++) { arg = ARG(n); DeRef(arg); /* dereference arguments */ .De .Ds .ta 3.5i if (!QUAL(arg) && TYPE(arg) == T_FILE) { if (n > 1) { putc('\en', f); fflush(f); } if ((BLKLOC(arg)->file.status & FS_WRITE) == 0) runerr(213, &arg); f = BLKLOC(arg)->file.fd; arg = nullstr; } else { if (n == 1 && (k_output.status & FS_WRITE) == 0) runerr(213, NULL); defany(&arg, &nullstr); if (cvstr(&arg, sbuf) == NULL) runerr(109, &arg); putstr(f, STRLOC(arg), STRLEN(arg)); } } .De .Ds .ta 3.5i putc('\en', f); fflush(f); if (STRLOC(arg) \*(>= sbuf && STRLOC(arg) < sbuf + MAXSTRING) { sneed(STRLEN(arg)); STRLOC(arg) = alcstr(STRLOC(arg), STRLEN(arg)); } ARG(0) = arg; } Procblock(write,\*b-1) .De The \-1 in the \fMProcblock\fR macro indicates that \fMwrite\fR takes an arbitrary number of arguments. .PP The following two routines are examples of typical functions that could be added to the run-time system using the technique described in Section 4. .PP The first of these routines, \fMseek\fR, interfaces to the C library routine \fMfseek\fR. .Ds .Ta 3.5i #include "../h/rt.h" /* * seek(file,\*boffset,\*bstart) - seek to offset byte from start in file. */ Xseek(nargs, arg3, arg2, arg1, arg0) int nargs; struct descrip arg3, arg2, arg1, arg0; { long l1, l2; int status; FILE *fd; long ftell(); DeRef(arg1); if (arg1.type != D_FILE) runerr(106); .Dd defint(&arg2, &l1, 0); defshort(&arg3, 0); fd = BLKLOC(arg1)->file.fd; if ((BLKLOC(arg1)->file.status == 0) || (fseek(fd, l1, arg3.value.integer) == -1)) fail(); mkint(ftell(fd), &arg0); } Procblock(seek,\*b3) .De The argument 3 in the \fMProcblock\fR macro indicates that \fMseek\fR takes three arguments. .PP The routine \fMgetenv\fR provides access to shell environment variables through the C library procedure \fMgetenv\fR. .Ds .ta 3.5i #include "../h/rt.h" /* * getenv(s) - return contents of environment variable s */ Xgetenv(nargs, arg1, arg0) int nargs; struct descrip arg1, arg0; { register char *p; register int len; char sbuf\^[MAXSTRING]; DeRef(&arg1); .Dd .ta 3.5i if (!QUAL(arg1)) /* check legality of argument */ runerr(103, &arg1); if (STRLEN(arg1) \*(<= 0 || STRLEN(arg1) \*(>= MAXSTRING) runerr(401, &arg1); qtos(&arg1, sbuf); /* convert argument to C-style string */ if ((p = getenv(sbuf)) != NULL) { /* get environment variable */ len = strlen(p); sneed(len); STRLEN(arg0) = len; STRLOC(arg0) = alcstr(p, len); } else /* fail if variable not in environment */ fail(); } Procblock(getenv,\*b-1) .De