BravoToTioga.mesa, Jim Morris, July 12, 1982 3:35 pm
last written by Bill Paxton, November 30, 1982 9:20 am
Last Edited by: Plass, March 16, 1983 11:18 am
Last Edited by: Mitchell, June 3, 1983 6:06 pm
DIRECTORY
BtoT,
UserExec,
Rope,
FileIO,
IO,
SafeStorage,
TiogaFileOps;
BravoToTioga: CEDAR PROGRAM
IMPORTS
R: Rope, FileIO,
IO,
T: TiogaFileOps, UserExec
EXPORTS BtoT
= {
ROPE: TYPE = R.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
--PROC [char: CHAR] RETURNS [CharClass] -- =
TRUSTED {
RETURN[
SELECT char
FROM
IO.SP, IO.TAB, IO.CR, ', => sepr,
'← => break,
ENDCASE => other]};
ForDebugging: SIGNAL = CODE;
Command: UserExec.CommandProc
--PROC [event: HistoryEvent, exec: ExecHandle, clientData: REF ANY ← NIL] RETURNS[ok: BOOLEAN ← TRUE, msg: ROPE ← NIL] -- =
BEGIN
in: IO.STREAM ← IO.CreateInputStreamFromRope[event.commandLine];
fileNames: LIST OF ROPE;
mfn: LIST OF ROPE ← LIST[NIL];
outputName:
ROPE ← in.GetToken[TokenProc];
BEGIN -- ENABLE IO.EndOfStream => GOTO SyntaxError;
IF outputName.Equal["←"] THEN outputName ← NIL
ELSE
IF NOT R.Equal["←", in.GetToken[TokenProc]] THEN GOTO SyntaxError;
fileNames ← mfn;
UNTIL in.EndOf[]
DO
mfn.first ← in.GetToken[TokenProc ! IO.EndOfStream => EXIT];
mfn.rest ← LIST[NIL]; -- append to list
mfn ← mfn.rest; -- move to next item to be filled
ENDLOOP;
IF fileNames.first=NIL THEN GOTO SyntaxError
ELSE msg ← IO.PutFToRope["%g written.\n", IO.rope[BravoToTioga[fileNames, outputName]]];
EXITS
SyntaxError => {
SIGNAL ForDebugging;
msg ← "Correct syntax: BravoToTioga [ output ] ← input1 input2 ...\n"};
END;
END;
NextParagraph:
PROC [str:
IO.
STREAM]
RETURNS [content: ROPE, paragraphData: ROPE, runCoding: ROPE] = {
CharProc: TYPE = PROC [char: CHAR] RETURNS [quit: BOOL ← FALSE, include: BOOL ← TRUE]
ToControlZ: IO.CharProc = {RETURN[char=R.Control['Z], char#R.Control['Z]]};
ToBackslash: IO.CharProc = {RETURN[char='\\ OR char=IO.CR, char#'\\ AND char#IO.CR]};
ToCR: IO.CharProc = {RETURN[char=IO.CR, char#IO.CR]};
content ← str.GetSequence[ToControlZ];
[] ← str.GetChar[]; -- throw away ^Z
paragraphData ← str.GetSequence[ToBackslash];
IF str.PeekChar[]='\\
THEN {
[] ← str.GetChar[]; runCoding ← str.GetSequence[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: NAT;
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 R.Digit[(digit ← s.Fetch[k])] DO NULL ENDLOOP;
present ← TRUE;
FOR j
IN [k .. sl)
WHILE
R.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;
IF s=NIL OR R.Equal[s, ""] THEN RETURN[FALSE];
i ← 0;
DO
FOR nj IN [i .. sl) WHILE s.Fetch[nj] IN ['0..'9] DO NULL ENDLOOP;
IF s.Fetch[nj]='. THEN {i ← nj+1; LOOP} ELSE RETURN [i#0];
ENDLOOP};
PSx: TYPE = [0..20);
FmtType: TYPE = {root, body, 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", "body", "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"];
BravoToTioga:
PUBLIC
PROC[fileNames:
LIST
OF
ROPE, outputName:
ROPE ←
NIL]
RETURNS[actualOutputName: ROPE] = {
st: IO.Handle;
ParentStack: ARRAY PSx OF RECORD[node: T.Ref, indent: INT, format: FmtType];
psp: PSx;
node: T.Ref;
format: FmtType;
root: T.Ref;
fileName, rootName: ROPE;
dotPos: INT;
indent: INT;
IF dotPos=fileName.Length[] THEN {rootName ← fileName; fileName ← fileName.Cat[".bravo"]}
ELSE rootName ← fileName.Substr[0, dotPos];
actualOutputName ← IF outputName=NIL THEN rootName.Cat[".tioga"] ELSE outputName;
FOR fl:
LIST
OF
ROPE ← fileNames, fl.rest
UNTIL (fileName ← fl.first)=
NIL
DO
dotPos ← fileName.SkipTo[0, "."];
IF dotPos=fileName.Length[] THEN {rootName ← fileName; fileName ← fileName.Cat[".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=NIL THEN rootName.Cat[".tioga"] ELSE outputName;
root ← T.CreateRoot[]; T.SetStyle[root, "Cedar"];
node ← root;
ParentStack[0] ← [node: root, indent: 0, format: root] };
st ← FileIO.Open[fileName, read];
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
c#NIL AND c.Fetch[0] = '( AND ParentStack[psp].format=body 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 ← body;
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 ← T.InsertAsLastChild[ParentStack[psp-1].node]),
indent: indent, format: format];
T.SetContents[node, c];
T.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 {
T.SetFormat[node, FmtRopes[format←logo]];
ParentStack[psp].format ← format}
ELSE {
x ← ComputeRun[l, 'f];
SELECT d FROM
'0 => NULL;
'1 => T.AddLooks[node, here, x, 's, root];
'2 => NULL;
'3 => T.AddLooks[node, here, x, 'm, root];
'4 => T.AddLooks[node, here, x, 'g, root];
'5 => T.AddLooks[node, here, x, 'l, root];
'6 => T.AddLooks[node, here, x, 'o, root];
'7 => {
T.AddLooks[node, here, x, 'o, root];
T.AddLooks[node, here, x, 's, root]};
'8 => T.AddLooks[node, here, x, 'f, root];
'9 => {
T.AddLooks[node, here, x, 'o, root];
T.AddLooks[node, here, x, 'x, root];
T.AddLooks[node, here, x, 'l, root]};
ENDCASE}};
't, 'o => c ← c.rest;
'b, 'i, 'z => {T.AddLooks[node, here, ComputeRun[l, R.Upper[c.first]], c.first, root]};
'u, 'd => {T.AddLooks[node, here, ComputeRun[l, 'D], c.first, root]};
ENDCASE;
c ← c.rest;
ENDLOOP;
here ← here + l.first.span;
ENDLOOP};
ENDLOOP;
st.Close[];
ENDLOOP;
T.Store[root, actualOutputName];
};
ParseRC:
PROC[r:
ROPE, l:
INT]
RETURNS[RCList] = {
ms: IO.STREAM ← IO.CreateInputStreamFromRope[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};
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};
UserExec.RegisterCommand["BravoToTioga", Command, "Conversion program: BravoToTioga [output] ← input1 input2 ... "];