ParseIntCodeImpl.mesa
Copyright Ó 1986, 1987, 1988, 1989, 1991, 1993 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) November 20, 1989 6:19:51 pm PST
JKF March 7, 1989 12:57:10 pm PST
Christian Jacobi, May 4, 1993 7:23 pm PDT
DIRECTORY
Atom USING [MakeAtom],
ConvertUnsafe USING [ToRope],
IntCodeDefs USING [Align, ArithClass, ArithClassKind, ArithSelector, BoolClass, ByteSequence, CaseList, CaseListRep, CedarSelector, Comparator, Count, FileId, Handler, HandlerRep, Label, LabelRep, LambdaKind, Location, LocationRep, LogicalId, MesaSelector, Node, NodeList, NodeListRep, NodeRep, nullFileId, nullVariableFlags, nullVariableId, Offset, OperRep, SourceRange, Var, VariableFlag, VariableFlags, VariableId, VarList, VarListRep, Word],
IntCodeUtils USING [Fetch, IdTab, IntToWord, NewIdTab, Store, WordToInt, zone],
IO USING [EndOfStream, GetChar, GetID, GetInt, GetRopeLiteral, PeekChar, PutChar, PutF1, PutRope, PutText, RopeFromROS, ROS, SkipWhitespace, STREAM, Value],
ParseIntCode USING [],
Rope USING [ActionType, Concat, FromChar, Length, Map, ROPE],
Target: TYPE MachineParms USING [bitsPerRef, bitsPerWord];
ParseIntCodeImpl: CEDAR PROGRAM
IMPORTS Atom, ConvertUnsafe, IntCodeUtils, IO, Rope
EXPORTS ParseIntCode
= BEGIN OPEN IntCodeDefs;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Types & errors
SyntaxError: PUBLIC ERROR [why: ROPE] = CODE;
signalStrange: BOOL ¬ FALSE;
StrangeCondition: SIGNAL = CODE;
For debugging
ParseContext: TYPE = REF ParseContextRep;
ParseContextRep: TYPE = RECORD [
stream: STREAM ¬ NIL,
idTab: IntCodeUtils.IdTab ¬ NIL,
labelTab: IntCodeUtils.IdTab ¬ NIL
];
bitsPerRef: NAT = Target.bitsPerRef;
defaultBits: NAT = Target.bitsPerWord;
The zone
z: ZONE ¬ IntCodeUtils.zone;
Testing routines
FromStream: PUBLIC PROC [st: STREAM] RETURNS [nodes: NodeList ¬ NIL] = {
idTab: IntCodeUtils.IdTab ¬ IntCodeUtils.NewIdTab[];
labelTab: IntCodeUtils.IdTab ¬ IntCodeUtils.NewIdTab[];
pc: ParseContext ¬ z.NEW[ParseContextRep ¬ [st, idTab, labelTab]];
tail: NodeList ¬ NIL;
DO
ENABLE IO.EndOfStream => EXIT;
list: NodeList ¬ z.NEW[NodeListRep ¬ [first: ParseNode[pc], rest: NIL]];
IF tail = NIL THEN nodes ¬ list ELSE tail.rest ¬ list;
tail ¬ list;
ENDLOOP;
};
ToRope: PROC [node: Node] RETURNS [ROPE] = {
ros: STREAM ¬ IO.ROS[];
out: OutputContext ¬ CreateOutputContext[ros];
PutNode[out, node];
RETURN [IO.RopeFromROS[ros]];
};
ToStream: PUBLIC PROC [st: STREAM, nodes: NodeList] = {
out: OutputContext ¬ CreateOutputContext[st];
FOR each: NodeList ¬ nodes, each.rest WHILE each # NIL DO
StartLine[out];
EndLine[out];
PutNode[out, each.first];
ENDLOOP;
};
Parsing routines (a parsing context is needed)
ParseNode: PROC [pc: ParseContext] RETURNS [node: Node ¬ NIL] = {
st: STREAM = pc.stream;
IF Insist[st, '{] THEN {
bits: INT ¬ ScanInt[st, -1];
IF PeekChar[st] # '} THEN {
kind: ATOM ¬ ScanKind[st];
SELECT kind FROM
$var => {
flags: VariableFlags ¬ ScanVariableFlags[st];
id: VariableId ¬ ParseVarId[pc];
loc: Location ¬ ParseLocation[pc];
useCache: BOOL ¬ FALSE;
IF bits < 0 THEN bits ¬ defaultBits;
IF id # nullVariableId AND (loc = NIL OR loc.kind = localVar) THEN {
This variable is a candidate for cacheing
prev: Var ¬ IF id = nullVariableId THEN NIL
ELSE
NARROW[IntCodeUtils.Fetch[pc.idTab, id]];
IF prev # NIL AND prev.location # NIL THEN {
IF loc = NIL THEN {node ¬ prev; GO TO oldVar};
NoteStrange[];
This is a conflicting location for a local variable!
};
IF prev # NIL THEN {
We just found the location!
prev.location ¬ loc;
node ¬ prev;
GO TO oldVar;
};
useCache ¬ TRUE;
};
node ¬ z.NEW[NodeRep.var ¬ [bits, var[flags, id, loc]]];
IF useCache THEN [] ¬ IntCodeUtils.Store[pc.idTab, id, node];
EXITS oldVar => {};
};
$const => {
constKind: ATOM ¬ ScanKind[st];
SELECT constKind FROM
$word => {
IF bits < 0 THEN bits ¬ defaultBits;
node ¬ z.NEW[NodeRep.const.word
¬ [bits, const[word[ScanWord[st]]]]];
};
$bytes => {
align: Align ¬ ScanInt[st];
byteSeq: ByteSequence ¬ ParseBytes[pc];
IF bits < 0 THEN node.bits ¬ 8*Rope.Length[byteSeq];
node ¬ z.NEW[NodeRep.const.bytes
¬ [bits, const[bytes[align, byteSeq]]]];
};
$refLiteral => {
const: REF NodeRep.const.refLiteral ¬ z.NEW[NodeRep.const.refLiteral
¬ [bitsPerRef, const[refLiteral[rope, NIL]]]];
refLitKind: ATOM ¬ ScanKind[st];
const.litKind ¬ SELECT refLitKind FROM
$rope => rope, $atom => atom, $refText => refText, $other => other,
ENDCASE => ERROR SyntaxError["illegal refLitKind"];
const.contents ¬ ParseBytes[pc];
node ¬ const;
};
$numLiteral => {
class: ArithClass ¬ ScanArithClass[st];
contents: ROPE ¬ ParseBytes[pc];
IF bits < 0 THEN bits ¬ defaultBits;
node ¬ z.NEW[NodeRep.const.numLiteral
¬ [bits, const[numLiteral[class, contents]]]];
};
ENDCASE => ERROR SyntaxError["illegal constant kind"];
};
$block => {
nodes: NodeList ¬ ParseNodeList[pc, '}];
IF bits < 0 THEN bits ¬ 0;
node ¬ z.NEW[NodeRep.block ¬ [bits, block[nodes]]];
};
$enable => {
handlerContext: Node ¬ ParseNode[pc];
handlerProc: Node ¬ ParseNode[pc];
handler: Handler ¬ z.NEW[HandlerRep
¬ [context: handlerContext, proc: handlerProc]];
nodes: NodeList ¬ ParseNodeList[pc, '}];
IF bits < 0 THEN bits ¬ 0;
node ¬ z.NEW[NodeRep.enable ¬ [bits, enable[handler, nodes]]];
};
$decl => {
var: Var ¬ ParseVar[pc];
init: Node ¬ ParseNode[pc];
node ¬ z.NEW[NodeRep.decl ¬ [0, decl[var: var, init: init]]];
};
$assign => {
lhs: Var ¬ ParseVar[pc];
rhs: Node ¬ ParseNode[pc];
IF bits < 0 THEN bits ¬ 0;
node ¬ z.NEW[NodeRep.assign ¬ [bits, assign[lhs: lhs, rhs: rhs]]];
};
$cond => {
caseHead: CaseList ¬ NIL;
caseTail: CaseList ¬ NIL;
WHILE PeekChar[st] # '} DO
IF Insist[st, '(] THEN {
key: ATOM ¬ ScanKind[st];
IF key = $test
THEN {
tests: NodeList ¬ ParseNodeList[pc, ')];
body: Node ¬ IF Insist[st, ')] THEN ParseNode[pc] ELSE NIL;
new: CaseList ¬ z.NEW[CaseListRep ¬ [tests, body, NIL]];
IF caseTail = NIL THEN caseHead ¬ new ELSE caseTail.rest ¬ new;
caseTail ¬ new;
}
ELSE ERROR SyntaxError["'test' expected"];
};
ENDLOOP;
IF bits < 0 THEN bits ¬ 0;
node ¬ z.NEW[NodeRep.cond ¬ [bits, cond[caseHead]]];
};
$label => {
label: Label ¬ ParseLabel[pc];
labNode: Node ¬ IF PeekChar[st] = '} THEN NIL ELSE ParseNode[pc];
IF bits < 0 THEN bits ¬ 0;
IF labNode # NIL THEN label.node ¬ labNode;
node ¬ z.NEW[NodeRep.label ¬ [bits, label[label]]];
};
$goto => {
dest: Label ¬ ParseLabel[pc, FALSE, TRUE];
backwards: BOOL ¬ ScanFlag[st];
IF backwards THEN dest.backTarget ¬ TRUE;
dest.jumpedTo ¬ TRUE;
node ¬ z.NEW[NodeRep.goto ¬
[0, goto[dest: dest, backwards: backwards]]];
};
$apply => {
proc: Node ¬ ParseNode[pc];
args: NodeList ¬ ParseNodeList[pc, '}, '!];
handler: Handler ¬ NIL;
IF Accept[st, '!] THEN {
handler ¬ z.NEW[HandlerRep];
handler.context ¬ ParseNode[pc];
handler.proc ¬ ParseNode[pc];
};
IF bits < 0 THEN bits ¬ defaultBits;
node ¬ z.NEW[NodeRep.apply ¬
[bits, apply[proc: proc, args: args, handler: handler]]];
};
$lambda => {
parent: Label ¬ ParseLabel[pc, TRUE, TRUE];
descBody: Var ¬ IF PeekChar[st] = '{ THEN ParseVar[pc] ELSE NIL;
kind: LambdaKind = SELECT ScanKind[st] FROM
$outer => outer,
$inner => inner,
$install => install,
$init => init,
$catch => catch,
$scope => scope,
$fork => fork,
ENDCASE => unknown;
bitsOut: INT ¬ ScanInt[st];
formalArgs: VarList ¬ ParseVarList[pc];
body: NodeList ¬ ParseNodeList[pc, '}];
node ¬ z.NEW[NodeRep.lambda ¬ [defaultBits, lambda[
parent: parent, kind: kind, descBody: descBody,
bitsOut: bitsOut, formalArgs: formalArgs, body: body]]];
};
$return =>
node ¬ z.NEW[NodeRep.return ¬
[0, return[ParseNodeList[pc, '}] ]]];
$oper => {
IF bits < 0 THEN bits ¬ 0;
node ¬ z.NEW[NodeRep.oper ¬
[bits, oper[z.NEW[OperRep ¬ ParseOperRep[pc]]]]];
};
$machineCode => {
IF bits < 0 THEN bits ¬ 0;
node ¬ z.NEW[NodeRep.machineCode ¬
[bits, machineCode[ ParseBytes[pc] ]]];
};
$module => {
vars: VarList ¬ ParseVarList[pc];
procs: NodeList ¬ ParseNodeList[pc, '}];
node ¬ z.NEW[NodeRep.module ¬
[0, module[vars: vars, procs: procs]]];
};
$source => {
source: SourceRange ¬ ScanSourceRange[st];
nodes: NodeList ¬ ParseNodeList[pc, '}];
IF bits < 0 THEN bits ¬ 0;
node ¬ z.NEW[NodeRep.source ¬
[bits, source[source: source, nodes: nodes]]];
};
$comment => {
msg: ByteSequence ¬ ParseBytes[pc];
node ¬ z.NEW[NodeRep.comment ¬ [0, comment[msg]]];
};
ENDCASE => ERROR SyntaxError ["illegal node kind"];
};
};
[] ¬ Insist[st, '}];
};
ParseNodeList: PROC [pc: ParseContext, stop1: CHAR ¬ '}, stop2: CHAR ¬ '!] RETURNS [head: NodeList ¬ NIL] = {
st: STREAM = pc.stream;
tail: NodeList ¬ NIL;
DO
SELECT PeekChar[st] FROM
stop1, stop2 => RETURN;
ENDCASE => {
node: Node ¬ ParseNode[pc];
new: NodeList ¬ z.NEW[NodeListRep ¬ [node, NIL]];
IF tail = NIL THEN head ¬ new ELSE tail.rest ¬ new;
tail ¬ new;
};
ENDLOOP;
};
ParseVar: PROC [pc: ParseContext] RETURNS [Var ¬ NIL] = {
node: Node ¬ ParseNode[pc];
IF node # NIL THEN
WITH node SELECT FROM
var: Var => RETURN [var];
ENDCASE => ERROR SyntaxError["var expected, node found"];
};
ParseVarList: PROC [pc: ParseContext] RETURNS [head: VarList ¬ NIL] = {
st: STREAM = pc.stream;
tail: VarList ¬ NIL;
[] ¬ Insist[st, '(];
WHILE NOT Accept[st, ')] DO
new: VarList ¬ z.NEW[VarListRep ¬ [ParseVar[pc], NIL]];
IF tail = NIL THEN head ¬ new ELSE tail.rest ¬ new;
tail ¬ new;
ENDLOOP;
};
ParseBytes: PROC [pc: ParseContext] RETURNS [ROPE ¬ NIL] = {
st: STREAM = pc.stream;
[] ¬ IO.SkipWhitespace[st];
SELECT IO.PeekChar[st] FROM
'" => RETURN [IO.GetRopeLiteral[st]];
ENDCASE;
};
ParseVarId: PROC [pc: ParseContext] RETURNS [VariableId] = {
st: STREAM = pc.stream;
RETURN [ScanInt[st]];
};
ParseLabel: PROC [pc: ParseContext, optional: BOOL ¬ FALSE, forceUsed: BOOL ¬ FALSE]
RETURNS [label: Label ¬ NIL] = {
st: STREAM = pc.stream;
SELECT TRUE FROM
Accept[st, '%] => {
id: LogicalId ¬ ScanId[st];
WITH IntCodeUtils.Fetch[pc.labelTab, id] SELECT FROM
lab: Label => {
IF forceUsed THEN lab.used ¬ TRUE;
RETURN [lab];
};
ENDCASE;
label ¬ z.NEW[LabelRep ¬ [
id: id, node: NIL, backTarget: FALSE, jumpedTo: FALSE, used: forceUsed]];
[] ¬ IntCodeUtils.Store[pc.labelTab, id, label];
RETURN [label];
};
NOT optional => ERROR SyntaxError["label expected"];
ENDCASE;
};
MesaSelectorFromSKind: PROC [sKind: ATOM] RETURNS [mesa: MesaSelector] = {
mesa ¬ SELECT sKind FROM
$addr => addr, $all => all,
$equal => equal, $notEqual => notEqual, $nilck => nilck,
$alloc => alloc, $free => free, $fork => fork, $join => join,
$monitorEntry => monitorEntry, $monitorExit => monitorExit,
$notify => notify, $broadcast => broadcast, $wait => wait,
$unnamedError => unnamedError, $unwindError => unwindError,
$abortedError => abortedError, $uncaughtError => uncaughtError,
$boundsError => boundsError, $narrowFault => narrowFault,
$signal => signal, $error => error,
$unwind => unwind, $resume => resume, $reject => reject,
$copyGlobal => copyGlobal, $startGlobal => startGlobal,
$restartGlobal => restartGlobal, $stopGlobal => stopGlobal, $checkInit => checkInit,
$globalFrame => globalFrame,
ENDCASE => ERROR SyntaxError["illegal mesa selector"];
RETURN[mesa];
};
ParseOperRep: PROC [pc: ParseContext] RETURNS [OperRep] = {
st: STREAM = pc.stream;
kind: ATOM ¬ ScanKind[st];
SELECT kind FROM
$arith => {
class: ArithClass ¬ ScanArithClass[st];
sKind: ATOM ¬ ScanKind[st];
select: ArithSelector ¬ SELECT sKind FROM
$add => add, $sub => sub, $mul => mul, $div => div,
$mod => mod, $pow => pow, $abs => abs, $neg => neg,
$min => min, $max => max,
ENDCASE => ERROR SyntaxError["illegal arith selector"];
RETURN [[arith[class, select]]];
};
$boolean => {
kind: ATOM ¬ ScanKind[st];
class: BoolClass ¬ SELECT kind FROM
$and => and, $not => not, $or => or, $xor => xor,
ENDCASE => ERROR SyntaxError["illegal boolean class"];
bits: Count ¬ ScanInt[st, defaultBits];
RETURN [[boolean[class, bits]]];
};
$code => {
label: Label ¬ ParseLabel[pc, FALSE, TRUE];
offset: INT ¬ ScanInt[st];
direct: BOOL ¬ ScanFlag[st, TRUE];
RETURN [[code[label, offset, direct]]];
};
$convert => {
to: ArithClass ¬ ScanArithClass[st];
from: ArithClass ¬ ScanArithClass[st];
RETURN [[convert[to: to, from: from]]];
};
$check => {
class: ArithClass ¬ ScanArithClass[st];
sKind: ATOM ¬ ScanKind[st];
sense: Comparator ¬ SELECT sKind FROM
$eq => eq, $lt => lt, $le => le, $ne => ne, $ge => ge, $gt => gt,
ENDCASE => ERROR SyntaxError["illegal comparator"];
RETURN [[check[class, sense]]];
};
$compare => {
class: ArithClass ¬ ScanArithClass[st];
sKind: ATOM ¬ ScanKind[st];
sense: Comparator ¬ SELECT sKind FROM
$eq => eq, $lt => lt, $le => le, $ne => ne, $ge => ge, $gt => gt,
ENDCASE => ERROR SyntaxError["illegal comparator"];
RETURN [[compare[class, sense]]];
};
$mesa => {
sKind: ATOM ¬ ScanKind[st];
mesa: MesaSelector ¬ MesaSelectorFromSKind[sKind];
RETURN [[mesa[mesa, ScanInt[st]]]];
};
$cedar => {
sKind: ATOM ¬ ScanKind[st];
cedar: CedarSelector ¬ SELECT sKind FROM
$simpleAssign => simpleAssign, $simpleAssignInit => simpleAssignInit,
$complexAssign => complexAssign, $complexAssignInit => complexAssignInit,
$new => new, $code => code,
$narrow => narrow, $referentType => referentType, $procCheck => procCheck,
ENDCASE => ERROR SyntaxError["illegal Cedar selector"];
RETURN [[cedar[cedar, ScanInt[st]]]];
};
$escape => {
id: LogicalId ¬ ScanId[st];
RETURN [[escape[id, ScanInt[st]]]];
};
ENDCASE => ERROR SyntaxError["illegal oper kind"];
};
ParseLocation: PROC [pc: ParseContext] RETURNS [loc: Location ¬ NIL] = {
st: STREAM = pc.stream;
IF Accept[st, '(] THEN {
kind: ATOM ¬ ScanKind[st];
SELECT kind FROM
$system => {
id: LogicalId ¬ ScanId[st];
loc ¬ z.NEW[LocationRep.system ¬ [system[id]]];
};
$globalVar => {
id: LogicalId ¬ ScanId[st];
loc ¬ z.NEW[LocationRep.globalVar ¬ [globalVar[id]]];
};
$localVar => {
id: LogicalId ¬ ScanId[st];
parent: Label ¬ ParseLabel[pc];
loc ¬ z.NEW[LocationRep.localVar ¬ [localVar[id, parent]]];
};
$register => {
id: LogicalId ¬ ScanId[st];
loc ¬ z.NEW[LocationRep.register ¬ [register[id]]];
};
$link => {
id: LogicalId ¬ ScanId[st];
loc ¬ z.NEW[LocationRep.link ¬ [link[id]]];
};
$stack => {
offset: Offset ¬ ScanOffset[st];
loc ¬ z.NEW[LocationRep.stack ¬ [stack[offset]]];
};
$deref => {
node: Node ¬ ParseNode[pc];
align: INT ¬ ScanInt[st, defaultBits];
loc ¬ z.NEW[LocationRep.deref ¬ [deref[node, align]]];
};
$indexed => {
base: Node ¬ ParseNode[pc];
index: Node ¬ ParseNode[pc];
loc ¬ z.NEW[LocationRep.indexed ¬ [indexed[base, index]]];
};
$field => {
offset: Offset ¬ ScanOffset[st];
node: Node ¬ ParseNode[pc];
loc ¬ z.NEW[LocationRep.field ¬ [field[node, offset, FALSE]]];
};
$xfield => {
This is only used by the little endian compiler
Added, ChJ, May 4, 1993
offset: Offset ¬ ScanOffset[st];
node: Node ¬ ParseNode[pc];
loc ¬ z.NEW[LocationRep.field ¬ [field[node, offset, TRUE]]];
};
$upLevel => {
link: Var ¬ ParseVar[pc];
reg: Var ¬ ParseVar[pc];
format: LogicalId ¬ ScanId[st];
loc ¬ z.NEW[LocationRep.upLevel ¬ [upLevel[link, reg, format]]];
};
$composite => {
list: NodeList ¬ ParseNodeList[pc, ')];
loc ¬ z.NEW[LocationRep.composite ¬ [composite[list]]];
};
$escape => {
id: LogicalId ¬ ScanId[st];
offset: Offset ¬ ScanOffset[st];
base: Node ¬ ParseNode[pc];
loc ¬ z.NEW[LocationRep.escape ¬ [escape[id, base, offset ]]];
};
$dummy =>
loc ¬ z.NEW[LocationRep ¬ [dummy[]]];
ENDCASE => ERROR SyntaxError["illegal location kind"];
[] ¬ Insist[st, ')];
};
};
Scanning routines (only a stream is needed)
ScanArithClass: PROC [st: STREAM] RETURNS [ArithClass] = {
kind: ArithClassKind ¬ signed;
precision: NAT ¬ 0;
checked: BOOL ¬ TRUE;
[] ¬ IO.SkipWhitespace[st];
SELECT IO.PeekChar[st] FROM
's, 'S => {kind ¬ signed; checked ¬ TRUE};
'u, 'U => {kind ¬ unsigned; checked ¬ FALSE};
'a, 'A => {kind ¬ address; checked ¬ FALSE};
'r, 'R => {kind ¬ real; checked ¬ TRUE};
'x, 'X => {
k: [0..ORD[LAST[ArithClassKind]]] ¬ 0;
[] ¬ IO.GetChar[st];
k ¬ ScanInt[st, ORD[LAST[ArithClassKind]] ];
kind ¬ VAL[k];
checked ¬ TRUE;
};
ENDCASE => ERROR SyntaxError["illegal arith class"];
[] ¬ IO.GetChar[st];
precision ¬ ScanInt[st, defaultBits];
checked ¬ ScanFlag[st, checked];
RETURN [[kind, checked, precision]];
};
ScanSourceRange: PROC [st: STREAM] RETURNS [SourceRange] = {
start: Offset ¬ ScanInt[st];
chars: Offset ¬ ScanInt[st];
fileNum: INT ¬ ScanInt[st];
file: FileId ¬ nullFileId;
RETURN [[start: start, chars: chars, file: fileNum]];
};
ScanInt: PROC [st: STREAM, default: INT ¬ 0] RETURNS [INT] = {
[] ¬ IO.SkipWhitespace[st];
SELECT IO.PeekChar[st] FROM
'-, IN ['0..'9] => {
RETURN [IO.GetInt[st]];
};
ENDCASE => RETURN [default];
};
ScanId: PROC [st: STREAM] RETURNS [LogicalId] = {
RETURN [ScanInt[st]];
Temporary!
};
ScanWord: PROC [st: STREAM] RETURNS [Word] = {
RETURN [IntCodeUtils.IntToWord[ScanInt[st]]];
Temporary!
};
ScanOffset: PROC [st: STREAM] RETURNS [Offset] = {
RETURN [ScanInt[st]];
Temporary!
};
ScanCount: PROC [st: STREAM] RETURNS [Offset] = {
card: LONG CARDINAL ¬ ScanInt[st];
RETURN [card];
Temporary!
};
ScanKind: PROC [st: STREAM, optional: BOOL ¬ FALSE] RETURNS [ATOM ¬ NIL] = {
[] ¬ IO.SkipWhitespace[st];
SELECT IO.PeekChar[st] FROM
IN ['A..'Z], IN ['a..'z] => {
id: ROPE ¬ IO.GetID[st];
RETURN [Atom.MakeAtom[id]];
};
ENDCASE => IF NOT optional THEN ERROR SyntaxError["number expected"];
};
ScanFlag: PROC [st: STREAM, default: BOOL ¬ FALSE] RETURNS [BOOL] = {
SELECT PeekChar[st] FROM
'T, 't => {[] ¬ IO.GetChar[st]; RETURN [TRUE]};
'F, 'f => {[] ¬ IO.GetChar[st]; RETURN [FALSE]};
ENDCASE => RETURN [default];
};
ScanVariableFlags: PROC [st: STREAM] RETURNS [VariableFlags] = {
flags: VariableFlags ¬ nullVariableFlags;
FOR flag: VariableFlag IN VariableFlag DO
SELECT PeekChar[st] FROM
'T, 't => flags[flag] ¬ TRUE;
'F, 'f => {};
ENDCASE => EXIT;
[] ¬ IO.GetChar[st];
ENDLOOP;
RETURN [flags];
};
PeekChar: PROC [st: STREAM] RETURNS [CHAR] = {
[] ¬ IO.SkipWhitespace[st];
RETURN [IO.PeekChar[st]];
};
Accept: PROC [st: STREAM, c: CHAR] RETURNS [BOOL] = {
[] ¬ IO.SkipWhitespace[st];
IF IO.PeekChar[st] = c THEN {[] ¬ IO.GetChar[st]; RETURN [TRUE]};
RETURN [FALSE];
};
Insist: PROC [st: STREAM, c: CHAR] RETURNS [BOOL] = {
[] ¬ IO.SkipWhitespace[st];
IF IO.PeekChar[st] = c THEN {[] ¬ IO.GetChar[st]; RETURN [TRUE]};
ERROR SyntaxError[Rope.Concat[Rope.FromChar[c], " expected"]];
};
Node output routines
PutNodeAuto: PROC [out: OutputContext, node: Node, break: BOOL ¬ FALSE] = {
PutNode[out, node, break OR IsComplex[node]];
};
PutNode: PROC
[out: OutputContext, node: Node, newLine: BOOL ¬ FALSE, space: BOOL ¬ TRUE] = {
IF newLine THEN EndLine[out];
IF space AND NOT out.empty THEN PutRopeLit[out, " "];
IF node = NIL THEN {PutRopeLit[out, "{}"]; RETURN};
PutRopeLit[out, "{"];
out.nodeLevel ¬ out.nodeLevel + 1;
SELECT node.kind FROM
var, apply =>
IF node.bits # defaultBits THEN PutInt[out, node.bits, "%g "];
block, enable, oper, machineCode, source, assign, cond =>
IF node.bits > 0 THEN PutInt[out, node.bits, "%g "];
ENDCASE => {};
WITH node SELECT FROM
var: REF NodeRep.var => PutVar[out, var, FALSE];
const: REF NodeRep.const => {
WITH const SELECT FROM
word: REF NodeRep.const.word => {
IF node.bits = defaultBits
THEN PutRopeLit[out, "const word "]
ELSE PutInt[out, node.bits, "%g const word "];
PutWord[out, word.word];
};
bytes: REF NodeRep.const.bytes => {
PutInt[out, node.bits, "%g const bytes "];
PutInt[out, bytes.align, "%g "];
PutBytes[out, bytes.bytes];
};
refLiteral: REF NodeRep.const.refLiteral => {
PutInt[out, node.bits, "%g const refLiteral "];
SELECT refLiteral.litKind FROM
rope => PutRopeLit[out, "rope "];
atom => PutRopeLit[out, "atom "];
refText => PutRopeLit[out, "refText "];
other => PutRopeLit[out, "other "];
ENDCASE => ERROR;
PutBytes[out, refLiteral.contents];
};
numLiteral: REF NodeRep.const.numLiteral => {
PutInt[out, node.bits, "%g const numLiteral "];
PutArithClass[out, numLiteral.class];
PutBytes[out, numLiteral.contents];
};
ENDCASE => ERROR;
};
block: REF NodeRep.block => {
nodeList: NodeList ¬ block.nodes;
PutRopeLit[out, "block"];
PutNodeList[out, block.nodes, TRUE];
};
enable: REF NodeRep.enable => {
PutRopeLit[out, "enable"];
PutNode[out, enable.handle.context, TRUE];
PutNode[out, enable.handle.proc, TRUE];
PutNodeList[out, enable.scope, TRUE];
};
decl: REF NodeRep.decl => {
var: Var = decl.var;
init: Node = decl.init;
SELECT TRUE FROM
decl.bits # 0 => NoteStrange[];
var = NIL => NoteStrange[];
init # NIL AND var.bits # init.bits => NoteStrange[];
ENDCASE;
PutRopeLit[out, "decl "];
PutVar[out, var, TRUE];
SELECT TRUE FROM
init = NIL => PutRopeLit[out, " {}"];
ENDCASE => PutNodeAuto[out, init];
};
assign: REF NodeRep.assign => {
break: BOOL ¬ IsComplex[assign.lhs, TRUE] OR IsComplex[assign.rhs, TRUE];
lhs: Node = assign.lhs;
rhs: Node = assign.rhs;
SELECT TRUE FROM
lhs = NIL => NoteStrange[];
rhs # NIL AND rhs.bits # lhs.bits => NoteStrange[];
ENDCASE;
PutRopeLit[out, "assign"];
PutNode[out, lhs, break];
PutNode[out, rhs, break];
};
cond: REF NodeRep.cond => {
delta: INTEGER ¬ 1;
PutRopeLit[out, "cond"];
FOR each: CaseList ¬ cond.cases, each.rest WHILE each # NIL DO
tests: NodeList ¬ each.tests;
EndLine[out];
IF tests # NIL
THEN {
first: Node ¬ tests.first;
break: BOOL ¬ tests.rest # NIL OR IsComplex[first, FALSE];
PutRopeLit[out, "(test"];
out.nodeLevel ¬ out.nodeLevel + 1;
FOR eachTest: NodeList ¬ tests, eachTest.rest WHILE eachTest # NIL DO
PutNode[out, eachTest.first, break];
ENDLOOP;
PutRopeLit[out, ")"];
out.nodeLevel ¬ out.nodeLevel - 1;
}
ELSE
PutRopeLit[out, "(test)"];
PutNode[out, each.body, TRUE];
EndLine[out];
ENDLOOP;
GO TO closeBlock;
};
label: REF NodeRep.label => {
lab: Label ¬ label.label;
PutRopeLit[out, "label "];
PutLabel[out, lab];
IF lab.node # NIL THEN PutNode[out, lab.node, TRUE];
};
goto: REF NodeRep.goto => {
PutRopeLit[out, "goto "];
PutLabel[out, goto.dest];
IF goto.dest = NIL THEN NoteStrange[];
IF goto.backwards THEN PutRopeLit[out, " T"];
};
apply: REF NodeRep.apply => {
handler: Handler ¬ apply.handler;
hasHandler: BOOL ¬ handler # NIL;
PutRopeLit[out, "apply"];
IF apply.proc = NIL THEN NoteStrange[];
PutNodeAuto[out, apply.proc, hasHandler];
IF apply.args # NIL THEN PutNodeList[out, apply.args, hasHandler];
IF hasHandler THEN {
EndLine[out];
PutRopeLit[out, "! "];
PutNodeAuto[out, handler.context, FALSE];
PutNodeAuto[out, handler.proc, FALSE];
};
};
lambda: REF NodeRep.lambda => {
PutRopeLit[out, "lambda "];
IF lambda.parent # NIL THEN {
PutLabel[out, lambda.parent];
PutRopeLit[out, " "];
};
IF lambda.descBody # NIL THEN {
PutRopeLit[out, "{"];
PutVar[out, lambda.descBody, FALSE];
PutRopeLit[out, "} "];
};
SELECT lambda.kind FROM
outer => PutRopeLit[out, "outer "];
inner => PutRopeLit[out, "inner "];
install => PutRopeLit[out, "install "];
init => PutRopeLit[out, "init "];
catch => PutRopeLit[out, "catch "];
scope => PutRopeLit[out, "scope "];
fork => PutRopeLit[out, "fork "];
ENDCASE => PutRopeLit[out, "unknown "];
PutInt[out, lambda.bitsOut, "%g "];
PutVarList[out, lambda.formalArgs, TRUE];
PutNodeList[out, lambda.body, TRUE];
};
return: REF NodeRep.return => {
IF return.bits # 0 THEN NoteStrange[];
PutRopeLit[out, "return"];
PutNodeList[out, return.rets];
};
oper: REF NodeRep.oper => {
PutRopeLit[out, "oper "];
WITH oper.oper SELECT FROM
arith: REF arith OperRep => {
PutRopeLit[out, "arith "];
PutArithClass[out, arith.class];
PutRopeLit[out, SELECT arith.select FROM
add => " add", sub => " sub", mul => " mul", div => " div",
mod => " mod", pow => " pow", abs => " abs", neg => " neg",
min => " min", max => " max",
ENDCASE => ERROR
];
};
boolean: REF boolean OperRep => {
PutRopeLit[out, "boolean "];
PutRopeLit[out, SELECT boolean.class FROM
and => "and", not => "not", or => "or", xor => "xor",
ENDCASE => ERROR
];
IF boolean.bits # defaultBits THEN PutInt[out, boolean.bits, " %g"];
};
code: REF code OperRep => {
PutRopeLit[out, "code "];
PutLabel[out, code.label];
IF code.offset # 0 THEN PutInt[out, code.offset, " %g"];
IF NOT code.direct THEN PutRopeLit[out, " F"];
};
convert: REF convert OperRep => {
PutRopeLit[out, "convert "];
PutArithClass[out, convert.to];
PutRopeLit[out, " "];
PutArithClass[out, convert.from];
};
check: REF check OperRep => {
PutRopeLit[out, "check "];
PutArithClass[out, check.class];
PutRopeLit[out, SELECT check.sense FROM
eq => " eq", lt => " lt", le => " le", ne => " ne", ge => " ge", gt => " gt",
ENDCASE => ERROR
];
};
compare: REF compare OperRep => {
PutRopeLit[out, "compare "];
PutArithClass[out, compare.class];
PutRopeLit[out, SELECT compare.sense FROM
eq => " eq", lt => " lt", le => " le", ne => " ne", ge => " ge", gt => " gt",
ENDCASE => ERROR
];
};
mesa: REF mesa OperRep => {
s: LONG STRING;
SELECT mesa.mesa FROM
addr => s ¬ "addr"; all => s ¬ "all"; equal => s ¬ "equal";
notEqual => s ¬ "notEqual"; nilck => s ¬ "nilck";
alloc => s ¬ "alloc"; free => s ¬ "free"; fork => s ¬ "fork"; join => s ¬ "join";
monitorEntry => s ¬ "monitorEntry"; monitorExit => s ¬ "monitorExit";
notify => s ¬ "notify"; broadcast => s ¬ "broadcast"; wait => s ¬ "wait";
unnamedError => s ¬ "unnamedError"; unwindError => s ¬ "unwindError";
abortedError => s ¬ "abortedError"; uncaughtError => s ¬ "uncaughtError";
boundsError => s ¬ "boundsError"; narrowFault => s ¬ "narrowFault";
signal => s ¬ "signal"; error => s ¬ "error";
unwind => s ¬ "unwind"; resume => s ¬ "resume"; reject => s ¬ "reject";
copyGlobal => s ¬ "copyGlobal"; startGlobal => s ¬ "startGlobal";
restartGlobal => s ¬ "restartGlobal"; stopGlobal => s ¬ "stopGlobal";
checkInit => s ¬ "checkInit"; globalFrame => s ¬ "globalFrame";
ENDCASE => ERROR;
PutRopeLit[out, "mesa "];
PutRopeLit[out, s];
IF mesa.info # 0 THEN PutInt[out, mesa.info, " %g"];
};
cedar: REF cedar OperRep => {
PutRopeLit[out, "cedar "];
PutRopeLit[out, SELECT cedar.cedar FROM
simpleAssign => "simpleAssign", simpleAssignInit => "simpleAssignInit",
complexAssign => "complexAssign", complexAssignInit => "complexAssignInit",
new => "new", code => "code",
narrow => "narrow", referentType => "referentType", procCheck => "procCheck",
ENDCASE => ERROR
];
IF cedar.info # 0 THEN PutInt[out, cedar.info, " %g"];
};
escape: REF escape OperRep => {
PutRopeLit[out, "escape "];
PutId[out, escape.escape];
IF escape.info # 0 THEN PutInt[out, escape.info, " %g"];
};
ENDCASE => ERROR;
};
machineCode: REF NodeRep.machineCode => {
PutRopeLit[out, "machineCode "];
PutBytes[out, machineCode.bytes];
};
module: REF NodeRep.module => {
PutRopeLit[out, "module "];
PutVarList[out, module.vars, TRUE];
PutNodeList[out, module.procs, TRUE];
EndLine[out];
GO TO closeBlock;
};
source: REF NodeRep.source => {
PutRopeLit[out, "source "];
PutInt[out, source.source.start];
PutRopeLit[out, " "];
PutInt[out, source.source.chars];
PutRopeLit[out, " "];
PutInt[out, source.source.file];
PutNodeList[out, source.nodes, TRUE];
GO TO closeBlock;
};
comment: REF NodeRep.comment => {
PutRopeLit[out, "comment "];
PutBytes[out, comment.bytes];
};
ENDCASE => ERROR;
PutRopeLit[out, "}"];
out.nodeLevel ¬ out.nodeLevel - 1;
EXITS closeBlock => {
PutRopeLit[out, "}"];
EndLine[out];
out.nodeLevel ¬ out.nodeLevel - 1;
};
};
PutNodeList: PROC [out: OutputContext, nodeList: NodeList, newLine: BOOL ¬ FALSE] = {
IF NOT newLine THEN newLine ¬ IsComplexList[nodeList];
WHILE nodeList # NIL DO
PutNode[out, nodeList.first, newLine];
nodeList ¬ nodeList.rest;
ENDLOOP;
};
PutVar: PROC [out: OutputContext, var: Var, defn: BOOL ¬ FALSE] = {
flags: VariableFlags ¬ var.flags;
loc: Location ¬ var.location;
showDetails: BOOL ¬ defn OR (loc # NIL AND loc.kind # localVar);
IF defn THEN {
PutRopeLit[out, "{"];
IF var.bits # defaultBits THEN PutInt[out, var.bits, "%g "];
};
PutRopeLit[out, "var"];
IF showDetails AND flags # nullVariableFlags THEN {
PutRopeLit[out, " "];
FOR flag: VariableFlag IN VariableFlag WHILE flags # nullVariableFlags DO
PutFlag[out, flags[flag]];
flags[flag] ¬ FALSE;
ENDLOOP;
};
PutVarId[out, var.id];
IF loc = NIL THEN NoteStrange[];
IF showDetails THEN PutLocation[out, var.location];
IF defn THEN PutRopeLit[out, "}"];
};
PutVarList: PROC [out: OutputContext, varList: VarList, defn: BOOL ¬ FALSE] = {
space: BOOL ¬ FALSE;
PutRopeLit[out, "("];
WHILE varList # NIL DO
IF defn
THEN {
IF space THEN PutRopeLit[out, " "];
PutVar[out, varList.first, TRUE]
}
ELSE PutNode[out, varList.first, FALSE, space];
varList ¬ varList.rest;
space ¬ TRUE;
ENDLOOP;
PutRopeLit[out, ")"];
};
PutArithClass: PROC [out: OutputContext, class: ArithClass] = {
checked: BOOL ¬ FALSE;
SELECT class.kind FROM
signed => PutRopeLit[out, "S"];
unsigned => PutRopeLit[out, "U"];
address => PutRopeLit[out, "A"];
real => {PutRopeLit[out, "R"]; checked ¬ TRUE};
ENDCASE => PutF1[out, "X%g ", [integer[ORD[class.kind]]]];
IF class.precision # defaultBits THEN
PutInt[out, class.precision];
IF class.checked # checked THEN
PutRopeLit[out, IF class.checked THEN "T" ELSE "F"];
};
PutFlag: PROC [out: OutputContext, flag: BOOL] = {
PutRopeLit[out, IF flag THEN "T" ELSE "F"];
};
PutVarId: PROC [out: OutputContext, varId: VariableId] = {
IF varId # nullVariableId THEN PutInt[out, varId, " %g"];
};
PutId: PROC [out: OutputContext, id: LogicalId] = {
PutInt[out, id];
Temporary!
};
PutLabel: PROC [out: OutputContext, label: Label] = {
IF label = NIL THEN PutRopeLit[out, "%0"] ELSE PutInt[out, label.id, "%%%g"];
};
PutInt: PROC [out: OutputContext, int: INT, form: ROPE ¬ NIL] = {
PutF1[out, IF form = NIL THEN "%g" ELSE form, [integer[int]] ];
};
PutWord: PROC [out: OutputContext, word: Word] = {
PutInt[out, IntCodeUtils.WordToInt[word]];
Temporary!
};
PutBytes: PROC [out: OutputContext, bytes: ByteSequence] = {
each: Rope.ActionType = {
[c: CHAR] RETURNS [quit: BOOLFALSE]
SELECT c FROM
'\", '\', '\\ => {IO.PutChar[st, '\\]; IO.PutChar[st, c]};
IN [040C..176C] => IO.PutChar[st, c];
ENDCASE => {
IO.PutChar[st, '\\];
IO.PutChar[st, '0+(ORD[c] / 100b)];
IO.PutChar[st, '0+((ORD[c] MOD 100b) / 10b)];
IO.PutChar[st, '0+(ORD[c] MOD 10b)];
};
};
st: STREAM = out.st;
IO.PutChar[st, '\"];
[] ¬ Rope.Map[base: bytes, action: each];
IO.PutChar[st, '\"];
};
PutLocation: PROC [out: OutputContext, loc: Location] = {
IF loc # NIL THEN {
PutRopeLit[out, " ("];
WITH loc SELECT FROM
system: REF LocationRep.system => {
PutRopeLit[out, "system "];
PutId[out, system.id];
};
globalVar: REF LocationRep.globalVar => {
PutRopeLit[out, "globalVar "];
PutId[out, globalVar.id];
};
localVar: REF LocationRep.localVar => {
PutRopeLit[out, "localVar "];
PutId[out, localVar.id];
PutRopeLit[out, " "];
PutLabel[out, localVar.parent];
};
register: REF LocationRep.register => {
PutRopeLit[out, "register "];
PutId[out, register.id];
};
link: REF LocationRep.link => {
PutRopeLit[out, "link "];
PutId[out, link.id];
};
stack: REF LocationRep.stack => {
PutRopeLit[out, "stack"];
IF stack.offset # 0 THEN PutInt[out, stack.offset, " %g"];
};
deref: REF LocationRep.deref => {
PutRopeLit[out, "deref"];
PutNode[out, deref.addr];
IF deref.align # defaultBits THEN PutInt[out, deref.align, " %g"];
};
indexed: REF LocationRep.indexed => {
PutRopeLit[out, "indexed"];
PutNode[out, indexed.base, TRUE];
PutNode[out, indexed.index, TRUE];
};
field: REF LocationRep.field => {
IF field.cross
THEN PutRopeLit[out, "xfield"] --little endian only ChJ, May 4, 1993
ELSE PutRopeLit[out, "field"];
PutInt[out, field.start, " %g"];
PutNode[out, field.base, TRUE];
};
upLevel: REF LocationRep.upLevel => {
PutRopeLit[out, "upLevel {"];
PutVar[out, upLevel.link, FALSE];
PutRopeLit[out, "} {"];
PutVar[out, upLevel.reg, FALSE];
PutInt[out, upLevel.format, "} %g"];
};
composite: REF LocationRep.composite => {
PutRopeLit[out, "composite"];
PutNodeList[out, composite.parts, TRUE];
};
escape: REF LocationRep.escape => {
PutRopeLit[out, "escape "];
PutId[out, escape.id];
IF escape.offset # 0 THEN PutInt[out, escape.offset, " %g"];
PutNode[out, escape.base, TRUE];
};
dummy: REF LocationRep.dummy => {
PutRopeLit[out, "dummy"];
};
ENDCASE => ERROR;
PutRopeLit[out, ")"];
};
};
IsComplexList: PROC [list: NodeList] RETURNS [BOOL] = {
count: NAT ¬ 0;
FOR each: NodeList ¬ list, each.rest WHILE each # NIL DO
IF IsComplex[each.first, TRUE] THEN RETURN [TRUE];
IF (count ¬ count + 1) > 3 THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE];
};
IsComplex: PROC [node: Node, inList: BOOL ¬ FALSE] RETURNS [BOOL] = {
IF node = NIL THEN RETURN [FALSE];
WITH node SELECT FROM
var: REF NodeRep.var => RETURN [IsComplexLocation[var.location, inList]];
const: REF NodeRep.const => RETURN [FALSE];
decl: REF NodeRep.decl => RETURN [IsComplex[decl.init]];
assign: REF NodeRep.assign => RETURN [inList OR IsComplex[assign.rhs]];
label: REF NodeRep.label => RETURN [label.label # NIL AND IsComplex[label.label.node]];
goto: REF NodeRep.goto => RETURN [FALSE];
return: REF NodeRep.return => RETURN [IsComplexList[return.rets]];
oper: REF NodeRep.oper => RETURN [FALSE];
source: REF NodeRep.source => RETURN [IsComplexList[source.nodes]];
ENDCASE => RETURN [TRUE];
};
IsComplexLocation: PROC [location: Location, inList: BOOL ¬ FALSE] RETURNS [BOOL] = {
IF location = NIL THEN RETURN [FALSE];
WITH location SELECT FROM
deref: REF LocationRep.deref => RETURN [inList OR IsComplex[deref.addr, TRUE]];
indexed: REF LocationRep.indexed => RETURN [TRUE];
field: REF LocationRep.field => RETURN [TRUE];
upLevel: REF LocationRep.upLevel => RETURN [TRUE];
composite: REF LocationRep.composite => RETURN [TRUE];
escape: REF LocationRep.escape => RETURN [TRUE];
ENDCASE => RETURN [FALSE];
};
Basic output routines
OutputContext: TYPE = REF OutputContextRep;
OutputContextRep: TYPE = RECORD [
nodeLevel: INTEGER ¬ 0,
writtenLevel: INTEGER ¬ 0,
empty: BOOL ¬ TRUE,
st: STREAM ¬ NIL,
text: REF TEXT ¬ NIL,
tos: STREAM ¬ NIL
];
CreateOutputContext: PROC [st: STREAM] RETURNS [OutputContext] = {
out: OutputContext ¬ z.NEW[OutputContextRep];
IF st # NIL
THEN {
out.st ¬ st;
}
ELSE {
ERROR;
};
RETURN [out];
};
EndLine: PROC [out: OutputContext, comment: BOOL ¬ FALSE] = {
SELECT TRUE FROM
out.st # NIL => IO.PutChar[out.st, '\n];
ENDCASE => ERROR;
out.empty ¬ TRUE;
};
StartLine: PROC [out: OutputContext] = {
SELECT TRUE FROM
out.st # NIL => {
st: STREAM ¬ out.st;
THROUGH [out.writtenLevel..out.nodeLevel) DO
IO.PutChar[st, ' ];
IO.PutChar[st, ' ];
ENDLOOP;
};
ENDCASE => ERROR;
out.empty ¬ FALSE;
};
PutChar: PROC [out: OutputContext, char: CHAR] = {
IF out.empty THEN StartLine[out];
SELECT TRUE FROM
out.st # NIL =>
IO.PutChar[out.st, char];
ENDCASE => ERROR;
};
PutRopeLit: PROC [out: OutputContext, s: LONG STRING] = {
IF out.empty THEN StartLine[out];
SELECT TRUE FROM
out.st # NIL =>
IO.PutRope[out.st, ConvertUnsafe.ToRope[s]];
ENDCASE => ERROR;
};
PutText: PROC [out: OutputContext, text: REF TEXT] = {
IF out.empty THEN StartLine[out];
SELECT TRUE FROM
out.st # NIL =>
IO.PutText[out.st, text];
ENDCASE => ERROR;
};
PutF1: PROC [out: OutputContext, format: ROPE, value: IO.Value] = {
IF out.empty THEN StartLine[out];
SELECT TRUE FROM
out.st # NIL =>
IO.PutF1[out.st, format, value];
ENDCASE => ERROR;
};
NoteStrange: PROC = {
IF signalStrange THEN SIGNAL StrangeCondition;
};
END.