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: ROPE ← NIL;
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:
BOOLEAN ←
FALSE, 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:
BOOLEAN ←
FALSE] = {
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:
ROPE ←
NIL]
RETURNS[actualOutputName:
ROPE ←
NIL, errors:
ROPE ←
NIL] = {
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};
};
'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.STREAM ← IO.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]