BravoToTioga.mesa, Jim Morris, July 12, 1982 3:35 pm
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bill Paxton, November 30, 1982 9:20 am
Plass, March 16, 1983 11:18 am
Mitchell, June 3, 1983 6:06 pm
Stewart, December 29, 1983 11:40 am
MBrown, December 31, 1983 9:35 am
Russ Atkinson (RRA) March 12, 1985 12:59:18 pm PST
Gunther, November 8, 1985 7:25:42 pm PST
DIRECTORY
Ascii USING [Control, Digit, Upper],
BtoT USING [],
Commander USING [CommandProc, Register],
CommandTool USING [Failed, ParseToList, ResolveRelativePath],
RefText USING [AppendChar, ObtainScratch, ReleaseScratch],
Rope USING [Concat, Equal, Fetch, FromRefText, IsEmpty, Length, ROPE, Substr],
FS USING [Error, StreamOpen],
IO USING [Backup, BreakProc, Close, CR, EndOf, EndOfStream, GetChar, PeekChar, RIS, SP, STREAM, TAB],
TiogaFileOps USING [AddLooks, CreateRoot, InsertAsLastChild, Ref, SetContents, SetFormat, SetStyle, Store];
BravoToTioga: CEDAR PROGRAM
IMPORTS Ascii, Commander, CommandTool, RefText, Rope, FS, IO, TiogaFileOps
EXPORTS BtoT =
BEGIN
Types
ROPE: TYPE = Rope.ROPE;
RCList: TYPE = LIST OF RECORD[coms: CList, span: INT];
CList: TYPE = LIST OF CHAR;
BtoTError: ERROR[c: CHAR, s1, s2: ROPE] = CODE;
TokenProc: IO.BreakProc = TRUSTED {
RETURN[SELECT char FROM
IO.SP, IO.TAB, IO.CR, ', => sepr,
'← => break,
ENDCASE => other];
};
CharProc: TYPE = PROC [char: CHAR] RETURNS [keep: BOOL];
PSx: TYPE = [0..20);
FmtType: TYPE = {root, block, center, contents, continuation, display, example, head, head1, head2, head3, head4, head5, indent, item, lead1, lead2, lead3, logo, memoHead, note, pageBreak, quote, reference, table, table1, table2, table3, title, subtitle};
FmtRopes: ARRAY FmtType OF ROPE = ["root", "block", "center", "contents", "continuation", "display", "example", "head", "head1", "head2", "head3", "head4", "head5", "indent", "item", "lead1", "lead2", "lead3", "logo", "memoHead", "note", "pageBreak", "quote", "reference", "table", "table1", "table2", "table3", "title", "subtitle"];
Procedures
Command: Commander.CommandProc = {
fileNames: LIST OF ROPE;
actualOutputName: ROPE;
errors: ROPE;
args: LIST OF ROPE;
nArgs: NAT;
outputName: ROPENIL;
IsArrow: PROC [r: ROPE] RETURNS [BOOL] = { RETURN[Rope.Equal[r, "←"]]; };
[list: args, length: nArgs] ← CommandTool.ParseToList[cmd ! CommandTool.Failed => {msg ← errorMsg; CONTINUE; }];
IF NOT msg.IsEmpty[] THEN RETURN[$Failure, msg];
FOR l: LIST OF Rope.ROPE ← args, l.rest WHILE l # NIL DO
l.first ← CommandTool.ResolveRelativePath[l.first];
ENDLOOP;
SELECT nArgs FROM
0 => GOTO SyntaxError;
1 => {
IF IsArrow[args.first] THEN GOTO SyntaxError;
outputName ← NIL;
fileNames ← args;
};
2 => {
IF IsArrow[args.rest.first] THEN GOTO SyntaxError;
IF IsArrow[args.first] THEN {
outputName ← NIL;
fileNames ← args.rest;
}
ELSE {
outputName ← NIL;
fileNames ← args;
};
};
ENDCASE => {
IF IsArrow[args.rest.first] THEN {
outputName ← args.first;
fileNames ← args.rest.rest;
}
ELSE {
outputName ← NIL;
fileNames ← args;
};
};
[actualOutputName: actualOutputName, errors: errors] ← BravoToTioga[fileNames: fileNames, outputName: outputName];
IF errors.Length[] > 0 THEN {
result ← $Failure;
msg ← errors;
}
ELSE msg ← Rope.Concat[actualOutputName, " written.\n"];
EXITS
SyntaxError => {
result ← $Failure;
msg ← "Correct syntax: BravoToTioga { outputFile ← } input1 input2 ...\n";
};
};
GetSequence: PROC [str: IO.STREAM, cp: CharProc] RETURNS [r: ROPE] = {
buf: REF TEXT ← RefText.ObtainScratch[100];
c: CHAR;
DO
c ← str.GetChar[];
IF cp[c]
THEN buf ← RefText.AppendChar[buf, c]
ELSE {
str.Backup[c];
EXIT;
};
ENDLOOP;
r ← Rope.FromRefText[buf];
RefText.ReleaseScratch[buf];
};
DotPos: PROC [r: Rope.ROPE] RETURNS [INT] = {
Returns position of the . denoting the extension
len: INT ← r.Length[];
FOR pos: INT DECREASING IN [0..len) DO
SELECT r.Fetch[pos] FROM
'. => RETURN[pos];
'/, '>, '] => RETURN[len];
ENDCASE;
ENDLOOP;
RETURN[len];
};
NextParagraph: PROC [str: IO.STREAM] RETURNS [content: ROPE, paragraphData: ROPE, runCoding: ROPE] = {
ToControlZ: CharProc = {RETURN[char#Ascii.Control['Z]]};
ToBackslash: CharProc = {RETURN[char#'\\ AND char#IO.CR]};
ToCR: CharProc = {RETURN[char#IO.CR]};
content ← GetSequence[str, ToControlZ];
[] ← str.GetChar[]; -- throw away ^Z
paragraphData ← GetSequence[str, ToBackslash];
IF str.PeekChar[]='\\
THEN {
[] ← str.GetChar[];
runCoding ← GetSequence[str, ToCR];
}
ELSE runCoding←"";
IF str.PeekChar[ ! IO.EndOfStream => CONTINUE] = IO.CR THEN [] ← str.GetChar[];
};
GetINT: PROC[stream: IO.STREAM] RETURNS [value: INT ← 0] = {
WHILE NOT stream.EndOf[] AND stream.PeekChar[] IN ['0..'9] DO
value ← value * 10 + (stream.GetChar[] - '0);
ENDLOOP;
};
Attr: PROC[s: ROPE, a: CHAR, dflt: INT ← 0] RETURNS [present: BOOLEANFALSE, val: INT] = {
i, j, k: INT;
digit: CHAR;
sl: INT = s.Length[];
val ← dflt;
FOR i IN [0..sl) DO
IF s.Fetch[i]=a THEN {
value: INT ← 0;
FOR k IN (i .. sl) UNTIL Ascii.Digit[(digit ← s.Fetch[k])] DO NULL ENDLOOP;
present ← TRUE;
FOR j IN [k .. sl) WHILE Ascii.Digit[(digit ← s.Fetch[j])] DO
val ← value ← value * 10 + (digit - '0);
ENDLOOP };
ENDLOOP;
};
SectionNum: PROC[s: ROPE] RETURNS [present: BOOLEANFALSE] = {
sl: INT = s.Length[];
i, nj: INT ← 0;
IF s.IsEmpty[] THEN RETURN[FALSE];
DO
FOR nj IN [i .. sl) WHILE s.Fetch[nj] IN ['0..'9] DO NULL ENDLOOP;
IF s.Fetch[nj] = '. THEN {
--i ← nj+1;
IF (i← nj+1) < sl THEN LOOP ELSE RETURN [i#0];
}
ELSE RETURN [i#0];
ENDLOOP;
};
BravoToTioga: PUBLIC PROC[fileNames: LIST OF ROPE, outputName: ROPENIL] RETURNS[actualOutputName: ROPENIL, errors: ROPENIL] = {
st: IO.STREAM;
ParentStack: ARRAY PSx OF RECORD[node: TiogaFileOps.Ref, indent: INT, format: FmtType];
psp: PSx;
node: TiogaFileOps.Ref;
format: FmtType;
root: TiogaFileOps.Ref;
fileName, rootName: ROPE;
dotPos: INT;
indent: INT;
FOR fl: LIST OF ROPE ← fileNames, fl.rest UNTIL fl = NIL DO
fileName ← fl.first;
dotPos ← DotPos[fileName];
IF Rope.Equal[Rope.Substr[fileName, dotPos], ".tioga", FALSE] THEN {
errors ← Rope.Concat[fileName, " is already a tioga file\n"];
RETURN[NIL, errors];
};
IF dotPos = fileName.Length[]
THEN {
rootName ← fileName;
fileName ← fileName.Concat[".bravo"];
}
ELSE rootName ← fileName.Substr[0, dotPos];
IF fl = fileNames THEN {
determine the output file name the first time and create Tioga root
actualOutputName ← IF outputName.IsEmpty[] THEN rootName.Concat[".tioga"] ELSE outputName;
root ← TiogaFileOps.CreateRoot[];
TiogaFileOps.SetStyle[root, "Cedar"];
node ← root;
ParentStack[0] ← [node: root, indent: 0, format: root] };
st ← NIL;
st ← FS.StreamOpen[fileName, $read
! FS.Error => {
IF error.group = $user THEN {
errors ← error.explanation;
CONTINUE;
};
}];
IF NOT errors.IsEmpty[] OR st = NIL THEN RETURN[NIL, errors];
psp ← 0;
UNTIL st.EndOf[] DO
c, p, r: ROPE;
[content: c, paragraphData: p, runCoding: r] ← NextParagraph[st];
indent ← Attr[p, 'l, 3000].val;
UNTIL psp<=1 OR indent >= ParentStack[psp].indent DO psp ← psp-1 ENDLOOP;
SELECT TRUE FROM-- decide on a format
NOT c.IsEmpty[] AND c.Fetch[0] = '( AND ParentStack[psp].format=block OR ParentStack[psp].format=item => format ← item;
Attr[p, 'e].val>=18 AND Attr[p, 'c].present AND Attr[p, 'k].val>=360 =>
format ← title;
Attr[p, 'k].val>=36 AND SectionNum[c] => format ← head;
Attr[p, 'c].present => format ← center;
ENDCASE => format ← block;
SELECT TRUE FROM
format=head AND ParentStack[psp].format#head => psp ← MAX[1, psp-1];
indent>ParentStack[psp].indent OR
ParentStack[psp].format=title OR
ParentStack[psp].format=head OR
(ParentStack[psp].format#item AND format=item) => psp ← MIN[LAST[PSx], psp+1];
ENDCASE;
ParentStack[psp] ← [
node: (node ← TiogaFileOps.InsertAsLastChild[ParentStack[psp-1].node]),
indent: indent, format: format];
TiogaFileOps.SetContents[node, c];
TiogaFileOps.SetFormat[node, FmtRopes[format]];
{
here: INT ← 0;
FOR l: RCList ← ParseRC[r, c.Length[]], l.rest UNTIL l=NIL DO
c: CList ← l.first.coms;
UNTIL c=NIL DO
SELECT c.first FROM
'f => {
d: CHAR = c.rest.first;
x: INT;
c ← c.rest;
IF d='2
THEN {
TiogaFileOps.SetFormat[node, FmtRopes[format←logo]];
ParentStack[psp].format ← format}
ELSE {
x ← ComputeRun[l, 'f];
SELECT d FROM
'0 => NULL;
'1 => TiogaFileOps.AddLooks[node, here, x, 's, root];
'2 => NULL;
'3 => TiogaFileOps.AddLooks[node, here, x, 'm, root];
'4 => TiogaFileOps.AddLooks[node, here, x, 'g, root];
'5 => TiogaFileOps.AddLooks[node, here, x, 'l, root];
'6 => TiogaFileOps.AddLooks[node, here, x, 'o, root];
'7 => {
TiogaFileOps.AddLooks[node, here, x, 'o, root];
TiogaFileOps.AddLooks[node, here, x, 's, root]};
'8 => TiogaFileOps.AddLooks[node, here, x, 'f, root];
'9 => {
TiogaFileOps.AddLooks[node, here, x, 'o, root];
TiogaFileOps.AddLooks[node, here, x, 'x, root];
TiogaFileOps.AddLooks[node, here, x, 'l, root]};
ENDCASE};
};
't, 'o =>
c ← c.rest;
'b, 'i, 'z =>
TiogaFileOps.AddLooks[node, here, ComputeRun[l, Ascii.Upper[c.first]], c.first, root];
'u, 'd =>
TiogaFileOps.AddLooks[node, here, ComputeRun[l, 'D], c.first, root];
ENDCASE;
c ← c.rest;
ENDLOOP;
here ← here + l.first.span;
ENDLOOP
};
ENDLOOP;
st.Close[];
ENDLOOP;
TiogaFileOps.Store[root, actualOutputName];
};
ParseRC: PROC[r: ROPE, l: INT] RETURNS[RCList] = {
ms: IO.STREAMIO.RIS[r];
P: PROC RETURNS [RCList] = {
IF ms.EndOf[] THEN RETURN [NIL];
{
c: CList = GetComs[];
i: INT = GetSpan[];
RETURN[CONS[[c, i], P[]]]
};
};
GetComs: PROC RETURNS [CList] = {
IF ms.EndOf[] THEN RETURN[NIL];
SELECT ms.PeekChar[] FROM
'o => {
[] ← ms.GetChar[];
{
n: INT = GetINT[ms]; -- n>128 implies subscript, n=0 implies neither
RETURN[CONS[(IF n=0 THEN 'D ELSE IF n>128 THEN 'd ELSE 'u), GetComs[]]]
}};
'f, 't=>
RETURN[CONS[ms.GetChar[], CONS[ms.GetChar[], GetComs[]]]];
'b, 'i, 'B, 'I, 'v, 'V, 'g, 'G, 's, 'S =>
RETURN[CONS[ms.GetChar[], GetComs[]]];
'u => {
[] ← ms.GetChar[];
RETURN[CONS['z, GetComs[]]];
};
'U => {
[] ← ms.GetChar[];
RETURN[CONS['Z, GetComs[]]];
};
' => {
[] ← ms.GetChar[];
RETURN[NIL];
};
IN ['0..'9] => RETURN[NIL];
ENDCASE => ERROR BtoTError[ms.PeekChar[], " unexpected in trailer ", r];
};
GetSpan: PROC RETURNS[n: INT] = {
IF ms.EndOf[] THEN RETURN[l];
n ← GetINT[ms];
l ← l-n};
RETURN[P[]];
};
ComputeRun: PROC[l: RCList, stop: CHAR] RETURNS [i: INT] = {
i ← l.first.span;
FOR x: RCList ← l.rest, x.rest UNTIL x=NIL DO
FOR t: CList ← x.first.coms, t.rest UNTIL t=NIL DO
IF t.first=stop THEN RETURN;
ENDLOOP;
i ← i+x.first.span;
ENDLOOP;
};
Commander.Register["BravoToTioga", Command, "Conversion program: BravoToTioga [output] ← input1 input2 ... "];
END.
December 29, 1983 11:40 am, Stewart, Cedar 5
December 31, 1983 9:22 am, MBrown, rope = NIL bugs
December 31, 1983 9:35 am, MBrown, CommandTool.ResolveRelativePath[args.first] -> CommandTool.ResolveRelativePath[l.first]