MetaCedarImpl.mesa
Copyright Ó 1984, 1985, 1987, 1990, 1991, 1992 Xerox Corporation. All rights reserved.
Michael Plass, November 23, 1992 7:30:56 pm PST
Russ Atkinson (RRA) May 11, 1987 6:19:05 pm PDT
DIRECTORY Ascii, Commander, CommanderOps, Convert, Rope, IO, PFS;
MetaCedarImpl: CEDAR PROGRAM
IMPORTS Commander, CommanderOps, Convert, Rope, IO, PFS
= BEGIN
ROPE: TYPE ~ Rope.ROPE;
RopePair: TYPE ~ RECORD [name, value: ROPE];
Scanner: TYPE ~ REF ScannerRep;
ScannerRep: TYPE ~ RECORD [
stream: IO.STREAM,
tokenKind: Kind,
inputBuffer: REF TEXT,
locationOfBufferStart: INT,
identifierScratch: REF TEXT,
tokenStart: NAT,
tokenLength: NAT
];
inputBufferSize: NAT ¬ 1024;
identifierScratchSize: NAT ¬ 120;
CreateScanner: PROC [stream: IO.STREAM] RETURNS [scanner: Scanner] ~ {
scanner ¬ NEW[ScannerRep ¬ [
stream: stream,
tokenKind: whitespace,
inputBuffer: NEW[TEXT[inputBufferSize]],
locationOfBufferStart: IO.GetIndex[stream],
identifierScratch: NEW[TEXT[identifierScratchSize]],
tokenStart: 0,
tokenLength: 0
]];
};
Kind: TYPE ~ {identifier, special, whitespace, newline, eof};
KindOf: PROC [c: CHAR] RETURNS [Kind] ~ {
SELECT c FROM
IN ['a..'z], IN ['A..'Z], IN ['0..'9], '$ => RETURN [identifier];
Ascii.SP, Ascii.TAB, Ascii.LF => RETURN [whitespace];
Ascii.CR => RETURN [newline];
ENDCASE => RETURN [special];
};
breakLoc: INT ¬ LAST[INT];
breakCount: INT ¬ 0;
GetToken: PROC [scanner: Scanner] ~ {
Gets the next token into inputBuffer[tokenStart] .. inputBuffer[tokenLength-1]
scanner.tokenStart ¬ scanner.tokenStart+scanner.tokenLength;
scanner.tokenLength ¬ 0;
IF scanner.tokenStart>=scanner.inputBuffer.length THEN RefillBuffer[scanner];
IF scanner.inputBuffer.length = 0 THEN {scanner.tokenKind ¬ eof; RETURN};
scanner.tokenKind ¬ KindOf[scanner.inputBuffer[scanner.tokenStart]];
IF scanner.tokenKind = special THEN {
scanner.tokenLength ¬ 1;
RETURN;
};
WHILE scanner.tokenStart+scanner.tokenLength<scanner.inputBuffer.length
AND KindOf[scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength]]
= scanner.tokenKind DO
IF scanner.locationOfBufferStart+scanner.tokenStart+scanner.tokenLength = breakLoc THEN
breakCount ¬ breakCount + 1;
scanner.tokenLength ¬ scanner.tokenLength + 1;
IF scanner.tokenStart + scanner.tokenLength = scanner.inputBuffer.length THEN {
IF IO.EndOf[scanner.stream] THEN RETURN;
RefillBuffer[scanner];
};
ENDLOOP;
};
RefillBuffer: PROC [scanner: Scanner] = {
FOR i: NAT IN [0..scanner.tokenLength) DO
scanner.inputBuffer[i] ¬ scanner.inputBuffer[i+scanner.tokenStart];
ENDLOOP;
scanner.inputBuffer.length ¬ scanner.tokenLength;
scanner.locationOfBufferStart ¬ scanner.locationOfBufferStart+scanner.tokenStart;
scanner.tokenStart ¬ 0;
[] ¬ scanner.stream.GetBlock[block: scanner.inputBuffer, startIndex: scanner.tokenLength, count: scanner.inputBuffer.maxLength-scanner.tokenLength];
};
Match: PROC [scanner: Scanner, key: REF READONLY TEXT] RETURNS [equal: BOOL] = {
i: NAT ¬ scanner.tokenStart;
IF key.length # scanner.tokenLength THEN RETURN [FALSE];
FOR j: NAT IN [0..key.length) DO
IF key[j]#scanner.inputBuffer[i] THEN RETURN [FALSE];
i¬i+1;
ENDLOOP;
RETURN [TRUE]
};
MetaError: ERROR [sourceLoc: INT] = CODE;
Run: PROC [fileNameStem: ROPE, outputNameStem: ROPE, binding: LIST OF RopePair] = {
in: IO.STREAM ¬ PFS.StreamOpen[PFS.PathFromRope[fileNameStem.Concat[".meta"]]];
out: IO.STREAM ¬ PFS.StreamOpen[PFS.PathFromRope["MetaProgram.mesa"], $create];
scanner: Scanner ¬ CreateScanner[in];
mode: {meta, quoteargs, text} ¬ text;
lineStart: BOOL ¬ TRUE;
copiedTextStart: INT ¬ -1;
copiedTextLength: INT ¬ 0;
WriteBindings: PROC [bindings: LIST OF RopePair] ~ {
IF bindings # NIL THEN {
WriteBindings[bindings.rest];
out.PutF["\t%g: INT ~ %g;\n", [rope[bindings.first.name]], [rope[bindings.first.value]]];
};
};
Flush: PROC ~ {
IF copiedTextLength > 0 THEN {
IO.PutF[out, "Ptxt[%g, %g];\n", [integer[copiedTextStart]], [integer[copiedTextLength]]];
};
copiedTextStart ¬ -1;
copiedTextLength ¬ 0;
};
CopyOut: PROC ~ {
IF mode#text THEN ERROR;
IF copiedTextStart+copiedTextLength = scanner.locationOfBufferStart+scanner.tokenStart THEN copiedTextLength ¬ copiedTextLength + scanner.tokenLength
ELSE IF scanner.tokenLength > 0 THEN {
Flush[];
copiedTextStart ¬ scanner.locationOfBufferStart+scanner.tokenStart;
copiedTextLength ¬ scanner.tokenLength;
};
lineStart ¬ FALSE;
};
CopyMetaOut: PROC ~ {
IF scanner.tokenLength > 0 THEN {
Flush[];
IO.PutBlock[out, scanner.inputBuffer, scanner.tokenStart, scanner.tokenLength];
};
lineStart ¬ FALSE;
};
arg: ROPE ¬ NIL;
unquoted: ROPE ¬ NIL;
CopyQuotedOut: PROC ~ {
arg ¬ Rope.Concat[arg, Rope.FromRefText[s: scanner.inputBuffer, start: scanner.tokenStart, len: scanner.tokenLength]];
};
CopyUnquotedOut: PROC [type: ROPE] ~ {
id: ROPE ~ Rope.FromRefText[s: scanner.inputBuffer, start: scanner.tokenStart, len: scanner.tokenLength];
arg ¬ Rope.Concat[arg, "%g"];
IF unquoted # NIL THEN unquoted ¬ Rope.Concat[unquoted, ", "];
unquoted ¬ Rope.Concat[unquoted, IO.PutFR["[%g[%g]]", [rope[type]], [rope[id]]]];
};
QFlush: PROC ~ {
Flush[];
IF unquoted # NIL THEN IO.PutRope[out, "IO.PutFLR["];
IF arg#NIL THEN IO.PutRope[out, Convert.RopeFromRope[arg]];
IF unquoted # NIL THEN {
IO.PutRope[out, ", LIST["];
IO.PutRope[out, unquoted];
IO.PutRope[out, "]]"];
};
unquoted ¬ arg ¬ NIL;
};
StartPlain: PROC = {
mode ¬ text;
};
startMetaCount: INT ¬ 0;
StartMeta: PROC [eof: BOOL ¬ FALSE] = {
Flush[];
mode ¬ meta;
};
MetaText: PROC [rope: ROPE] = {
IF mode#text THEN ERROR;
Flush[];
IO.PutRope[out, rope];
};
inxx: BOOL ¬ FALSE;
nestCount: NAT ¬ 0;
qnest: INT ¬ 0;
beginStackTop: NAT ¬ 0;
beginStack: ARRAY [0..50) OF NAT;
GetToken[scanner];
IO.PutF[out, boilerPlateBegin, [rope[outputNameStem]], [rope[outputNameStem]], [rope[fileNameStem]]];
WriteBindings[binding];
StartPlain[];
WHILE scanner.tokenKind # eof DO
SELECT mode FROM
meta => {
SELECT scanner.tokenKind FROM
special => SELECT scanner.inputBuffer[scanner.tokenStart] FROM
' => StartPlain[];
' => MetaError[scanner.locationOfBufferStart+scanner.tokenStart];
ENDCASE => CopyMetaOut[];
ENDCASE => CopyMetaOut[];
};
quoteargs => {
SELECT scanner.tokenKind FROM
special => SELECT scanner.inputBuffer[scanner.tokenStart] FROM
', => {
IF qnest = 0
THEN {QFlush[]; IO.PutRope[out, ", "]}
ELSE CopyQuotedOut[]
};
'(, '{, '[ => {qnest ¬ qnest + 1; CopyQuotedOut[]};
'), '} => {
qnest ¬ qnest - 1;
IF qnest < 0
THEN MetaError[scanner.locationOfBufferStart+scanner.tokenStart]
ELSE CopyQuotedOut[]
};
'] => {
qnest ¬ qnest - 1;
IF qnest < 0
THEN {mode ¬ text; QFlush[]; IO.PutRope[out, "]; "]}
ELSE CopyQuotedOut[]
};
'& => {
GetToken[scanner];
CopyUnquotedOut["integer"];
};
'% => {
GetToken[scanner];
CopyUnquotedOut["rope"];
};
'?, ', ' => {
MetaError[scanner.locationOfBufferStart+scanner.tokenStart];
};
ENDCASE => CopyQuotedOut[];
ENDCASE => CopyQuotedOut[];
};
text => {
SELECT scanner.tokenKind FROM
special => SELECT scanner.inputBuffer[scanner.tokenStart] FROM
' => StartMeta[];
' => MetaError[scanner.locationOfBufferStart+scanner.tokenStart];
'(, '{, '[ => {nestCount ¬ nestCount + 1; CopyOut[]};
'), '}, '] => {nestCount ¬ nestCount - 1; CopyOut[]};
'& => {
Flush[];
GetToken[scanner];
IO.PutRope[out, "PutInt["];
CopyMetaOut[];
IO.PutRope[out, "]; "];
};
'% => {
Flush[];
GetToken[scanner];
IO.PutRope[out, "stream.PutRope["];
CopyMetaOut[];
IO.PutRope[out, "]; "];
};
'? => {
Flush[];
GetToken[scanner];
CopyMetaOut[];
GetToken[scanner];
IF scanner.inputBuffer[scanner.tokenStart] # '[ THEN MetaError[scanner.locationOfBufferStart+scanner.tokenStart];
IO.PutRope[out, "["];
qnest ¬ 0;
mode ¬ quoteargs;
};
ENDCASE => CopyOut[];
newline => {CopyOut[]; lineStart ¬ TRUE};
whitespace => IF lineStart THEN NULL ELSE CopyOut[];
identifier => {
CopyOut[];
SELECT TRUE FROM
Match[scanner, "DO"],
Match[scanner, "FROM"],
Match[scanner, "BEGIN"] => {
MetaText["Begin[];"];
beginStack[beginStackTop] ¬ nestCount;
beginStackTop ¬ beginStackTop + 1;
};
Match[scanner, "ENDLOOP"],
Match[scanner, "ENDCASE"],
Match[scanner, "END"] => {
MetaText["End[];"];
beginStackTop ¬ beginStackTop - 1;
IF beginStack[beginStackTop] # nestCount THEN MetaError[scanner.locationOfBufferStart+scanner.tokenStart];
Something funny about the way things match up.
};
ENDCASE => NULL;
};
ENDCASE => CopyOut[];
};
ENDCASE => ERROR;
GetToken[scanner];
ENDLOOP;
IF mode=meta THEN StartPlain[];
StartMeta[eof: TRUE];
IO.PutRope[out, boilerPlateEnd];
IO.Close[in];
IO.Close[out];
};
boilerPlateBegin: ROPE = "
DIRECTORY Rope, IO, PFS;
MetaProgram: CEDAR PROGRAM IMPORTS Rope, IO, PFS = {
ROPE: TYPE ~ Rope.ROPE;
TwoToThe: PROC [a: INT] RETURNS [b: INT] ~ {
IF a < 0 THEN RETURN [0];
b ← 1;
THROUGH [0..a) DO b ← b+b ENDLOOP;
};
ModuleName: PROC ~ {IO.PutRope[stream, \"%g\"]};
stream: IO.STREAM ~ PFS.StreamOpen[PFS.PathFromRope[\"%g.mesa\"], $create];
nest: NAT ← 0;
Begin: PROC ~ {nest ← nest + 1};
End: PROC ~ {nest ← nest - 1};
PutInt: PROC [i: INT] ~ {
IO.PutF1[stream, IF i < 0 THEN \"(%%g)\" ELSE \"%%g\", [integer[i]]]};
metaSource: ROPE ~ PFS.RopeOpen[PFS.PathFromRope[\"%g.meta\"]].rope;
Ptxt: PROC [start, length: CARDINAL] ~ {
s: INT ← start;
FOR i: INT IN [start..start+length) DO
SELECT metaSource.Fetch[i] FROM
'{, '[, '( => Begin[];
'}, '], ') => End[];
'\r => {
stream.PutRope[metaSource.Substr[s, i-s+1]];
FOR i: NAT IN [0..nest) DO stream.PutChar['\t] ENDLOOP;
s ← i+1;
};
ENDCASE => NULL;
ENDLOOP;
stream.PutRope[metaSource.Substr[s, start+length-s]];
};
";
boilerPlateEnd: ROPE = "IO.Close[stream]}.";
GetCmdToken: PROC [stream: IO.STREAM] RETURNS [rope: ROPE ¬ NIL] ~ {
rope ¬ IO.GetTokenRope[stream ! IO.EndOfStream => CONTINUE].token;
};
MetaCedarRunCommand: Commander.CommandProc ~ {
ENABLE PFS.Error => CommanderOps.Failed[error.explanation];
c: IO.STREAM ¬ IO.RIS[cmd.commandLine];
stem: ROPE ¬ GetCmdToken[c];
outputStem: ROPE ¬ GetCmdToken[c];
binding: LIST OF RopePair ¬ NIL;
out: IO.STREAM ¬ cmd.out;
tmpDir: ROPE ~ Rope.Cat["/tmp/meta-", stem, "/"];
IF outputStem = NIL THEN outputStem ¬ stem;
out.PutF["Creating %g.mesa from %g.meta:\n", [rope[outputStem]], [rope[stem]]];
UNTIL c.EndOf DO
ropePair: RopePair ¬ [name: GetCmdToken[c], value: GetCmdToken[c]];
IF ropePair.name = NIL OR ropePair.value = NIL THEN EXIT;
cmd.out.PutF[" %g: INT ~ %g;\n", [rope[ropePair.name]], [rope[ropePair.value]]];
binding ¬ CONS[ropePair, binding];
ENDLOOP;
result ¬ CommanderOps.DoCommand[Rope.Concat["mkdir /tmp/meta-", stem], cmd];
result ¬ CommanderOps.DoCommand[
Rope.Cat["Copy ", tmpDir, "MetaCedarTemp.meta ← ", stem, ".meta"],
cmd];
IF result = $Failure THEN RETURN;
BEGIN
InTmp: PROC ~ {
result ¬ CommanderOps.DoCommand["WriteMesaPlain MetaCedarTemp.meta", cmd];
IF result = $Failure THEN RETURN;
IO.PutRope[out, "Writing MetaProgram.mesa . . . "];
Run["MetaCedarTemp", outputStem, binding !
MetaError => {
result ¬ $Failure;
msg ¬ IO.PutFR1["Something fishy at position &g of MetaCedarTemp.meta\n", [integer[sourceLoc]]]
}
];
IF result = $Failure THEN RETURN;
out.PutRope["Done.\n"];
result ¬ CommanderOps.DoCommand["bringover -o BasicTime.mob Rope.mob IO.mob PFS.mob /Cedar/Top/PFS.df", cmd];
result ¬ CommanderOps.DoCommand["mkdir sun4", cmd];
result ¬ CommanderOps.DoCommand["Mako -~o MetaProgram.mesa", cmd];
IF result = $Failure THEN RETURN;
result ¬ CommanderOps.DoCommand["Run -a MetaProgram", cmd];
IF result = $Failure THEN RETURN;
result ¬ CommanderOps.DoCommand[Rope.Cat["TiogaMesa ", outputStem, ".mesa"], cmd];
};
PFS.DoInWDir[PFS.PathFromRope[tmpDir], InTmp];
END;
IF result = $Failure THEN RETURN;
result ¬ CommanderOps.DoCommand[Rope.Cat["Copy ", tmpDir, outputStem, ".mesa"], cmd];
IF result = $Failure THEN RETURN;
result ¬ CommanderOps.DoCommand[Rope.Cat["Mako ", outputStem, ".mesa"], cmd];
};
Commander.Register["MetaCedar", MetaCedarRunCommand, "Simpleton macro expander"];
END.