CedarCode.Mesa
Sturgis, September 13, 1989 11:40:06 am PDT
Last changed by Theimer on August 25, 1989 12:01:56 pm PDT
Hopcroft July 26, 1989 12:06:05 pm PDT
Last tweaked by Mike Spreitzer on January 9, 1992 8:51 am PST
Started: November 5, 1988 3:31:42 pm PST
Sturgis: November 5, 1988 3:51:22 pm PST
DIRECTORY
CirioTypes USING[Code, CompilerContext, Node, OperationsBody, ParseTree, ParseTreeBody, Type],
IO USING[STREAM],
Rope USING[ROPE];
CedarCode: CEDAR DEFINITIONS SHARES CirioTypes =
BEGIN
Code: TYPE = CirioTypes.Code;
Type: TYPE = CirioTypes.Type;
CC: TYPE = CirioTypes.CompilerContext;
Node: TYPE = CirioTypes.Node;
Operator: TYPE = ATOM;
Some of the operators are of the form $x where x is one of the following:
{plus, minus, div, mult, mod, le, lt, eq, ne, gt, ge, and, or, not, max, min, size, bits, bytes, units, words, first, last, dot, assign, uparrow, leftSideuparrow, extractId, selectId, apply, index, cons};
The code will be created by a compiler which accepts object style parse trees. We include their declaration here at this time (January 25, 1989) to avoid circularities in the definitions files. Later the correct structure for these files will have to be decided.
We treat a parse tree as an object to which certain compile functions can be applied. Notice that there will be a flip/flop between general syntactic code (e.g., RHSBinaryOp) and code specific to a specific parse tree implementation (e.g., due to an object call on CompileForRHS). Each time a routine in the specific parse tree implementation wishes to call a general syntactic routine, it must first call CreateParseTree with the next deeper level of tree structure. Thus, a ParseTree will be created for each node in the actual tree.
ParseTree: TYPE = CirioTypes.ParseTree;
ParseTreeBody: TYPE = CirioTypes.ParseTreeBody;
Useful.
ShowCode: PROC [Code] RETURNS [Rope.ROPE];
The routines in this interface are designed to create code for an expression stack interpreter.
ConcatCode: PROC[c1, c2: Code] RETURNS[Code];
CopyCode: PROC [Code] RETURNS [Code];
CodeToDoUnpopedCond: PROC[test, trueCase, falseCase: Code] RETURNS[Code];
CodeToPop: PROC[nPops: INT] RETURNS[Code];
CodeToDup: PROC RETURNS [Code];
NullCode: PROC RETURNS[Code];
CodeToLoadGlobalFrame: PROC [name: Rope.ROPE, nToSkip: INT] RETURNS [Code];
CodeToLoadNameScope: PROC RETURNS[Code];
Pushes the current name scope on top the stack
CodeToGetNameContext: PUBLIC PROC[scopeIndex: CARDINAL] RETURNS[Code];
Replaces the top of stack (a name scope) with the particular name context node specified by scopeIndex.
CodeToLoadContentsOfAMNode: PROC[node: Node] RETURNS[Code];
Pushes the contents of the node on top of the stack.
CodeToLoadAMNode: PROC[node: Node] RETURNS[Code];
Pushes the node on top of the stack.
CodeToMakeAMNode: PROC[sourceType: Type] RETURNS[Code];
replaces the top value on the stack with an amNode holding that value.
CodeToCoerce: PROC[sourceType, targetType: Type] RETURNS[Code];
replaces the top value on the stack with another value of a different type.
CodeToDoBinaryOp: PROC[op: Operator, left, right: Type] RETURNS[Code];
Replaces the top two values on the stack with a value computed from the given values. op is one of {plus, minus, div, mult, mod, le, lt, eq, ne, gt, ge, max, min}. The argument types are left and right.
CodeToDoUnaryOp: PROC[op: Operator, type: Type] RETURNS[Code];
replaces the top value on the stack with a value computed from the given value. op is one of {plus, minus}. The argument type is type.
CodeToBuildRecord: PROC[nFields: INT, rcdType: Type] RETURNS[Code];
replace the top n values on the stack with a record containing those values.
CodeToStoreUnpopped: PROC[indirectType: Type, sourceType: Type] RETURNS[Code];
stores the top value on the stack at the location specified by the second value on the stack (assumed to be an indirect). Replaces the top two values on the stack with the top value.
CodeToLoadThroughIndirect: PROC[indirectType: Type] RETURNS[Code];
replaces the top value (assumed to be an indirect) with the contents of its target.
CodeToExtractField: PROC[id: Rope.ROPE, type: Type] RETURNS[Code];
replaces the top value (assumed to have a field structure including the named field) with the contents of the named field.
CodeToSelectField: PROC[id: Rope.ROPE, indirectType: Type] RETURNS[Code];
replaces the top value (assumed to be an indirect with a field structure including the named field) with an indirect to the named field.
CodeToDoApply: PROC[operatorType: Type, operandType: Type] RETURNS[Code];
Applies the operator (assumed to be one down in the stack) to the operand (assumed to be the top of the stack). Replaced these top two values of the stack with the result of the application. [apply is used for procedures, signals, errors, arrays, array descriptors, and sequences.]
CodeToDoIndex: PROC[indirectOperatorType: Type, operandType: Type] RETURNS[Code];
Applies the operator (assumed to be one down in the stack) to the operand (assumed to be the top of the stack). Replaced these top two values of the stack with the result of the application. [index is used for indirects to arrays, array descriptors, and sequences. It returns an indirect to the indexed entry.]
CodeToSelectNestedBlock: PROC[set: INT, depth: INT, indirectType: Type] RETURNS[Code];
Only applies to frames. Assumes that the top of the stack holds an indirect to a frame. The result is the block that is nested depth levels from the outermost. That is, depth=0 produces the outermost block of local variables. (The args and results records may be obtained by using CodeToSelectField["&args", ..] or CodeToSelectField["&results", ..].)
The first argument selects between enumeration constant blocks (set = 1) and variable blocks (set = 0).
and here is the interpreter
Interpret: PROC[code: Code, cc: CC, debug: IO.STREAMNIL] RETURNS[Node];
The interpreter works with nodes. (Node is declared in CirioTypes.)
CreateCedarNode: PROC[ops: REF OperationsBody, type: Type, data: REF ANY] RETURNS[Node];
GetTypeOfNode: PROC[node: Node] RETURNS[Type];
most nodes just return their type. Indirects to unions check their current target type and return an appropriate indirect type. This type is very fleeting. Should only be used under appropriate controls by the interpreter.
GetCurrentTypeOfNode: PROC[node: Node, cc: CC] RETURNS[Type];
GetDataFromNode: PROC[node: Node] RETURNS[REF ANY];
Coerce: PROC[sourceType, targetType: Type, node: Node, cc: CC] RETURNS[Node];
First checks for Conforms[GetTypeOfNode[node], sourceType] and Conforms[sourceType, targetType]. If both hold, then node is returned. If not, then then node.ops.coerce[sourceType, targetType, node, cc] is returned.
Comment: it is not clear what the rules are on which nodes have to provide which operations. Usually an operation need be provided only if the associated type generates code that provokes it. However, getCurrentType is envoked during an AMNodeSelectField. Hence, it seems that whenever selectField is needed, then getCurrentType is also needed.
OperationsBody: TYPE = CirioTypes.OperationsBody;
Some of these object procs are directly available
AdvanceNameScope: PROC [node: Node, cc: CC] RETURNS [Node];
ExamineParseTree: PROC[node: Node, cc: CC] RETURNS[ParseTree];
ApplyBinaryOp: PROC[op: Operator, leftType, rightType: Type, leftNode, rightNode: Node, cc: CC] RETURNS[Node];
ApplyUnaryOp: PROC[op: Operator, type: Type, node: Node, cc: CC] RETURNS[Node];
StoreThroughIndirectNode: PROC[valType: Type, valNode: Node, indirectType: Type, indirectNode: Node, cc: CC];
LoadThroughIndirectNode: PROC[indirectType: Type, indirectNode: Node, cc: CC] RETURNS[Node];
some implementations may choose to retain the indirect representation of the contents of the node. One can force a pure copy by using the following routine.
ForceNodeIn: PROC[type: Type, node: Node, cc: CC] RETURNS[Node];
Some implementations of nodes that are supposed to be pure values are really holding an indirect to the value. This procedure returns a node that holds a pure instance of the value, creating one if needed, otherwise returning the original node.
ExtractFieldFromNode: PROC[id: Rope.ROPE, type: Type, node: Node, cc: CC] RETURNS[Node];
SelectFieldFromNode: PROC[id: Rope.ROPE, indirectType: Type, indirectNode: Node, cc: CC] RETURNS[Node];
NodeAsIndex: PROC[type: Type, node: Node, cc: CC] RETURNS[CARD];
This operation is only supported by the discrete numeric types and by enumerated types. The returned value is the relative position of the node value within its (subrange) type. i.e., roughly valof(node) - first(typeof(node)).
ShowNode: PROC[to: IO.STREAM, node: Node, depth, width: INT, cc: CC];
ShowNodeBracketed: PROC[to: IO.STREAM, node: Node, depth, width: INT, cc: CC];
BreakShowNode: PROC[to: IO.STREAM, node: Node, depth, width: INT, cc: CC, sep: Rope.ROPENIL];
GetNodeRepresentation: PROC[node: Node, cc: CC] RETURNS[REF ANY];
This operation is only supplied by some nodes and it is implementation dependent. That is, even for a single type, the structure of returned representation may depend upon the target world to which the value belongs.
Certain kinds of nodes can be created. I am not sure where this interface should lie, or where the implementations should lie.
AMNodeConstructRecordNode: PROC[rcdType: Type, fields: LIST OF Node, cc: CC] RETURNS[Node];
implemented in CedarCodeImpl. This should be an object proc of something.
AMNodeConstructArrayNode: PROC[arrayType: Type, entries: LIST OF Node, cc: CC] RETURNS[Node];
implemented in CedarCodeImpl. This should be an object proc of something.
END..