MODULE TFXRef;
IMPORT
TS := TFTypeSys, TFAOParser, MultiLogger, Streams, Trace, Commands, KernelLog, Kernel,
TextUtilities, Texts, ST := TFScopeTools, S := BimboScanner, Strings, Files, UTF8Strings, TFClasses, Dates,
TFDocGenerator;
CONST
KindNoStart = 0;
KindComment = 1;
KindDeclaration = 2;
KindUse = 3;
TYPE
Range = RECORD
a, b : LONGINT;
kind : LONGINT;
no : TS.NamedObject;
END;
NamedObjectArray = POINTER TO ARRAY OF TS.NamedObject;
LocalExternalUsesSet = OBJECT
VAR nof : LONGINT;
items : NamedObjectArray;
PROCEDURE &Init;
BEGIN
nof := 0;
NEW(items, 1024);
END Init;
PROCEDURE Add(x : TS.NamedObject);
VAR i : LONGINT;
BEGIN
i := 0;
WHILE (i < nof) & (items[i] # x) DO INC(i) END;
IF i < nof THEN RETURN END;
IF nof = LEN(items) THEN Grow END;
items[nof] := x;
INC(nof);
END Add;
PROCEDURE Grow;
VAR temp : NamedObjectArray;
i : LONGINT;
BEGIN
NEW(temp, LEN(items) * 2);
FOR i := 0 TO LEN(items) - 1 DO
temp[i] := items[i]
END;
items := temp
END Grow;
END LocalExternalUsesSet;
StringList = POINTER TO ARRAY OF Strings.String;
GlobalUse = OBJECT
VAR
items : StringList;
nofItems : LONGINT;
PROCEDURE &Init;
BEGIN
NEW(items, 16);
nofItems := 0;
END Init;
PROCEDURE AddFile(CONST filename : ARRAY OF CHAR);
BEGIN
IF nofItems = LEN(items) THEN Grow END;
items[nofItems] := Strings.NewString(filename);
INC(nofItems)
END AddFile;
PROCEDURE Grow;
VAR temp : StringList;
i : LONGINT;
BEGIN
NEW(temp, LEN(items) * 2);
FOR i := 0 TO LEN(items) - 1 DO
temp[i] := items[i]
END;
items := temp
END Grow;
END GlobalUse;
VAR
ml : MultiLogger.LogWindow;
globalUses : TFClasses.StringHashMap;
ranges : POINTER TO ARRAY OF Range;
localUses : LocalExternalUsesSet;
currentAuthor : ARRAY 128 OF CHAR;
currentPurpose : ARRAY 4096 OF CHAR;
PROCEDURE MakeRange(from, to, kind : LONGINT; no : TS.NamedObject);
BEGIN
ranges[from].kind := kind;
ranges[from].a := from;
ranges[from].b := to;
ranges[from].no := no;
END MakeRange;
PROCEDURE DumpConst(scope : TS.Scope; c : TS.Const);
BEGIN
CheckExpression(c.expression, scope)
END DumpConst;
PROCEDURE DumpObject(o : TS.Class);
BEGIN
IF o.scope.superQualident # NIL THEN
CheckDesignator(o.scope.superQualident, o.container);
END;
DumpDeclarations(o.scope);
END DumpObject;
PROCEDURE DumpArray(a : TS.Array; scope : TS.Scope);
BEGIN
IF a.expression # NIL THEN CheckExpression(a.expression, scope) END;
DumpType(a.base, scope)
END DumpArray;
PROCEDURE DumpRecord(r : TS.Record);
BEGIN
DumpDeclarations(r.scope);
END DumpRecord;
PROCEDURE DumpProcedure(p : TS.ProcedureType);
BEGIN
END DumpProcedure;
PROCEDURE CheckExpressionList(e : TS.ExpressionList; sig : TS.ProcedureSignature; scope : TS.Scope);
BEGIN
WHILE e # NIL DO
CheckExpression(e.expression, scope);
e := e.next
END
END CheckExpressionList;
PROCEDURE CheckExpression(e : TS.Expression; scope : TS.Scope);
VAR t : TS.Type;
sr : TS.SetRange;
BEGIN
IF e = NIL THEN KernelLog.String("Expression is NIL"); RETURN END;
IF e.kind = TS.ExpressionPrimitive THEN
IF e.basicType = TS.BasicSet THEN
sr := e.setValue.setRanges;
WHILE sr # NIL DO
IF sr.a # NIL THEN CheckExpression(sr.a, scope) END;
IF sr.b # NIL THEN CheckExpression(sr.b, scope) END;
sr := sr.next
END;
END;
ELSIF e.kind = TS.ExpressionUnary THEN
CheckExpression(e.a, scope);
ELSIF e.kind = TS.ExpressionBinary THEN
CheckExpression(e.a, scope);
IF e.op # TS.OpIs THEN CheckExpression(e.b, scope)
ELSE
t := ST.FindType(e.b.designator, scope);
CheckDesignator(e.b.designator, scope);
IF t = NIL THEN KernelLog.String("pos = "); KernelLog.Int(e.b.designator(TS.Ident).pos.a, 0); KernelLog.String(" Type not found ") END;
END
ELSIF e.kind = TS.ExpressionDesignator THEN
CheckDesignator(e.designator, scope)
END;
END CheckExpression;
PROCEDURE CheckDesignator(d : TS.Designator; scope : TS.Scope);
VAR no: TS.NamedObject;
curScope : TS.Scope;
type, temptype : TS.Type;
first : BOOLEAN;
s : ARRAY 64 OF CHAR;
m : TS.Module;
te : TS.ExpressionList;
lastpos : LONGINT;
PROCEDURE Check(id : TS.Ident; no : TS.NamedObject);
BEGIN
IF no = NIL THEN RETURN END;
localUses.Add(no);
MakeRange(id.pos.a, id.pos.b, KindUse, no);
END Check;
BEGIN
first := TRUE;
curScope := scope;
WHILE d # NIL DO
IF d IS TS.Ident THEN
lastpos := d(TS.Ident).pos.a;
TS.s.GetString(d(TS.Ident).name, s);
IF first & (s = "SELF") THEN
curScope := scope.parent;
WHILE (curScope.parent # NIL) & (curScope.owner # NIL) &
~((curScope.owner IS TS.Class) OR (curScope.owner IS TS.Module)) DO
curScope := curScope.parent
END;
IF curScope = NIL THEN
KernelLog.String("SELF could not be resolved"); KernelLog.Ln;
END;
ELSIF first & (s = "SYSTEM") THEN
d := d.next;
IF d # NIL THEN
IF d IS TS.Ident THEN
TS.s.GetString(d(TS.Ident).name, s);
IF s = "VAL" THEN
d := d.next;
IF d # NIL THEN
IF d IS TS.ActualParameters THEN
te := d(TS.ActualParameters).expressionList;
IF te # NIL THEN
IF te.expression.kind = TS.ExpressionDesignator THEN
temptype := ST.FindType(te.expression.designator, scope);
IF temptype = NIL THEN KernelLog.String("pos = "); KernelLog.Int(te.expression.designator(TS.Ident).pos.a, 0); KernelLog.String(" Type not found ") END;
END;
te := te.next;
CheckExpression(te.expression, scope);
ELSE
KernelLog.String("type arameter expeced"); KernelLog.Ln;
END
ELSE
KernelLog.String("parameters expeced"); KernelLog.Ln;
END
ELSE
KernelLog.String("Pos= "); KernelLog.Int(d(TS.Ident).pos.a, 0); KernelLog.String(s); KernelLog.String("Ident expeced"); KernelLog.Ln;
END
END
ELSE
KernelLog.String(s); KernelLog.String("Ident expeced"); KernelLog.Ln;
END
ELSE
KernelLog.String("Pos= "); KernelLog.Int(d(TS.Ident).pos.a, 0); KernelLog.String(s); KernelLog.String("incomplete SYSTEM call"); KernelLog.Ln;
END
ELSE
IF curScope # NIL THEN
no := curScope.Find(s, first);
IF (no # NIL) & (d.next # NIL) & (d.next IS TS.Dereference) & (no IS TS.ProcDecl) THEN
no.scope.parent.FixSuperScope;
IF no.scope.parent.super # NIL THEN
no := no.scope.parent.super.Find(s, FALSE)
ELSE KernelLog.String(" super is NIL"); KernelLog.String(s); KernelLog.Ln;
END
END;
Check(d(TS.Ident), no);
IF no # NIL THEN
IF no IS TS.Var THEN
type := ST.DealiaseType(no(TS.Var).type);
IF type # NIL THEN
IF type.kind = TS.TRecord THEN curScope := type.record.scope
ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
END
ELSIF no IS TS.ProcDecl THEN
IF no(TS.ProcDecl).signature # NIL THEN
type := ST.DealiaseType(no(TS.ProcDecl).signature.return);
IF type # NIL THEN
IF type.kind = TS.TRecord THEN curScope := type.record.scope
ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
END
END;
ELSIF no IS TS.Import THEN
m := TS.GetModule(no(TS.Import));
IF m # NIL THEN
curScope := m.scope;
END
ELSIF no IS TS.Const THEN
IF d.next # NIL THEN
END
END
ELSE
END
ELSE
KernelLog.String("no scope"); KernelLog.Ln;
END
END
ELSIF d IS TS.Dereference THEN IF d.next # NIL THEN d := d.next END;
ELSIF d IS TS.Index THEN
IF (type # NIL) & (type.kind = TS.TPointer) THEN
type := ST.DealiaseType(type.pointer.type) END;
IF (type = NIL) OR ( type.kind # TS.TArray) THEN
IF type # NIL THEN ST.ShowType(type) END;
KernelLog.String("Type is not an array pos= "); KernelLog.Int(lastpos, 0); KernelLog.Ln
ELSE
type := ST.DealiaseType(type.array.base);
IF type # NIL THEN
IF type.kind = TS.TRecord THEN curScope := type.record.scope
ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
END
END;
CheckExpressionList(d(TS.Index).expressionList, NIL, scope);
ELSIF d IS TS.ActualParameters THEN
IF no # NIL THEN
IF no IS TS.ProcDecl THEN
CheckExpressionList(d(TS.ActualParameters).expressionList, no(TS.ProcDecl).signature, scope)
ELSIF (no IS TS.Var) THEN
type := ST.DealiaseType(no(TS.Var).type);
IF (type # NIL) & (type.kind = TS.TProcedure) THEN
IF type.procedure = NIL THEN
KernelLog.String("no(TS.Var).type.procedure"); KernelLog.Ln;
ELSIF type.procedure.signature = NIL THEN
KernelLog.String("no(TS.Var).type.procedure.signature"); KernelLog.Ln;
ELSE
CheckExpressionList(d(TS.ActualParameters).expressionList, type.procedure.signature, scope)
END;
ELSE
IF d(TS.ActualParameters).expressionList # NIL THEN
IF d(TS.ActualParameters).expressionList.next # NIL THEN
KernelLog.String("lastpos= "); KernelLog.Int(lastpos, 0);
KernelLog.String(" Can only guard for one type at once."); KernelLog.Ln
ELSE
IF d(TS.ActualParameters).expressionList.expression.kind = TS.ExpressionDesignator THEN
type := ST.DealiaseType(ST.FindType(d(TS.ActualParameters).expressionList.expression.designator, scope));
IF type # NIL THEN
IF type.kind = TS.TRecord THEN curScope := type.record.scope
ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
END;
CheckDesignator(d(TS.ActualParameters).expressionList.expression.designator, scope);
ELSE
KernelLog.String("Type expected"); KernelLog.Ln
END
END
ELSE
KernelLog.String("Expressionlist ist NIL"); KernelLog.Ln
END
END
ELSE
END
ELSE
CheckExpressionList(d(TS.ActualParameters).expressionList, NIL, scope)
END
END;
first := FALSE;
IF type # NIL THEN
IF type.kind = TS.TPointer THEN type := ST.DealiaseType(type.pointer.type) END;
IF type # NIL THEN
IF type.kind = TS.TRecord THEN curScope := type.record.scope
ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
END
END;
d := d.next
END
END CheckDesignator;
PROCEDURE DumpType*(t : TS.Type; scope : TS.Scope);
BEGIN
CASE t.kind OF
|TS.TAlias : CheckDesignator(t.qualident, scope)
|TS.TObject : DumpObject(t.object)
|TS.TArray : DumpArray(t.array, scope);
|TS.TPointer : DumpType(t.pointer.type, scope)
|TS.TRecord : DumpRecord(t.record);
|TS.TProcedure : DumpProcedure(t.procedure)
ELSE
Trace.String("Unknown Type"); Trace.String("t.kind= "); Trace.Int(t.kind, 0); Trace.Ln;
END
END DumpType;
PROCEDURE DumpCases(case : TS.Case; scope : TS.Scope);
VAR cr : TS.CaseRange;
BEGIN
WHILE case # NIL DO
cr := case.caseRanges;
WHILE cr # NIL DO
CheckExpression(cr.a, scope);
IF cr.b # NIL THEN CheckExpression(cr.b, scope) END;
cr := cr.next
END;
IF case.statements # NIL THEN DumpStatementSequence(case.statements, scope) END;
case := case.next
END;
END DumpCases;
PROCEDURE DumpTypeDecl(t : TS.TypeDecl; scope : TS.Scope);
BEGIN
DumpType(t.type, scope);
END DumpTypeDecl;
PROCEDURE DumpVar(v : TS.Var; scope : TS.Scope);
BEGIN
DumpType(v.type, scope);
END DumpVar;
PROCEDURE DumpStatementSequence(s : TS.Statement; scope : TS.Scope);
VAR ts : TS.Statement;
BEGIN
WHILE s # NIL DO
IF s IS TS.Assignment THEN
CheckDesignator(s(TS.Assignment).designator, scope);
CheckExpression(s(TS.Assignment).expression, scope);
ELSIF s IS TS.ProcedureCall THEN
CheckDesignator(s(TS.ProcedureCall).designator, scope);
ELSIF s IS TS.IFStatement THEN
CheckExpression(s(TS.IFStatement).expression, scope);
DumpStatementSequence(s(TS.IFStatement).then, scope);
ts := s(TS.IFStatement).else;
IF ts # NIL THEN
DumpStatementSequence(ts, scope);
END;
ELSIF s IS TS.WHILEStatement THEN
CheckExpression(s(TS.WHILEStatement).expression, scope);
DumpStatementSequence(s(TS.WHILEStatement).statements, scope);
ELSIF s IS TS.REPEATStatement THEN
DumpStatementSequence(s(TS.REPEATStatement).statements, scope);
CheckExpression(s(TS.REPEATStatement).expression, scope);
ELSIF s IS TS.LOOPStatement THEN
DumpStatementSequence(s(TS.LOOPStatement).statements, scope);
ELSIF s IS TS.FORStatement THEN
CheckDesignator(s(TS.FORStatement).variable, scope);
CheckExpression(s(TS.FORStatement).fromExpression, scope);
CheckExpression(s(TS.FORStatement).toExpression, scope);
IF s(TS.FORStatement).byExpression # NIL THEN
CheckExpression(s(TS.FORStatement).byExpression, scope);
END;
DumpStatementSequence(s(TS.FORStatement).statements, scope);
ELSIF s IS TS.EXITStatement THEN
ELSIF s IS TS.RETURNStatement THEN
IF s(TS.RETURNStatement).expression # NIL THEN CheckExpression(s(TS.RETURNStatement).expression, scope) END;
ELSIF s IS TS.AWAITStatement THEN
CheckExpression(s(TS.AWAITStatement).expression, scope);
ELSIF s IS TS.StatementBlock THEN
DumpStatementSequence(s(TS.StatementBlock).statements, scope);
ELSIF s IS TS.WITHStatement THEN
CheckDesignator(s(TS.WITHStatement).variable, scope);
CheckDesignator(s(TS.WITHStatement).type, scope);
DumpStatementSequence(s(TS.WITHStatement).statements, scope);
ELSIF s IS TS.CASEStatement THEN
CheckExpression(s(TS.CASEStatement).expression, scope);
DumpCases(s(TS.CASEStatement).cases, scope);
IF s(TS.CASEStatement).else # NIL THEN
DumpStatementSequence(s(TS.CASEStatement).else, scope)
END;
END;
NoteCommentRanges(s.preComment);
NoteCommentRanges(s.postComment);
s := s.next
END
END DumpStatementSequence;
PROCEDURE CheckSignature(sig : TS.ProcedureSignature; scope : TS.Scope);
VAR i : LONGINT; cur : TS.NamedObject; t : TS.Type;
BEGIN
IF sig = NIL THEN RETURN END;
IF sig.return # NIL THEN DumpType(sig.return, scope) END;
IF sig.params # NIL THEN
t := NIL;
FOR i := 0 TO sig.params.nofObjs - 1 DO
cur := sig.params.objs[i];
NoteDeclaration(cur);
IF cur IS TS.Var THEN IF t # cur(TS.Var).type THEN DumpType(cur(TS.Var).type, scope) END; t := cur(TS.Var).type
ELSE KernelLog.String("non- variable as a parameter"); KernelLog.Ln
END
END
END
END CheckSignature;
PROCEDURE DumpProcDecl(p : TS.ProcDecl);
VAR s : TS.Statement;
cur : TS.NamedObject; i : LONGINT;
BEGIN
CheckSignature(p.signature, p.scope.parent);
DumpDeclarations(p.scope);
IF p.scope.ownerBody # NIL THEN
s := p.scope.ownerBody;
DumpStatementSequence(s, p.scope)
END;
END DumpProcDecl;
PROCEDURE DumpDeclarations(d : TS.Scope);
VAR i : LONGINT;
last, cur : TS.NamedObject;
BEGIN
IF d = NIL THEN RETURN END;
FOR i := 0 TO d.elements.nofObjs - 1 DO
cur := d.elements.objs[i];
CommentsFromNamedObject(cur);
NoteDeclaration(cur);
IF cur IS TS.Const THEN
DumpConst(d, cur(TS.Const))
ELSIF cur IS TS.TypeDecl THEN
DumpTypeDecl(cur(TS.TypeDecl), d);
ELSIF cur IS TS.Var THEN
DumpVar(cur(TS.Var), d)
ELSIF cur IS TS.ProcDecl THEN
DumpProcDecl(cur(TS.ProcDecl))
ELSIF cur IS TS.Import THEN
END;
last := cur;
END
END DumpDeclarations;
PROCEDURE NoteCommentRanges(comments : TS.Comments);
VAR cur : TS.Comment;
r : Streams.StringReader;
token : ARRAY 32 OF CHAR;
BEGIN
IF comments = NIL THEN RETURN END;
cur := comments.first;
WHILE cur # NIL DO
IF (currentAuthor = "") & (Strings.Pos("AUTHOR", cur.str^) >= 0) THEN
IF Strings.Pos("PURPOSE", cur.str^) >= 0 THEN
NEW(r, LEN(cur.str^));
r.Set(cur.str^);
WHILE r.res # Streams.EOF DO
r.SkipWhitespace;
r.Token(token);
r.SkipWhitespace;
IF token = "AUTHOR" THEN
r.String(currentAuthor);
KernelLog.String("currentAuthor= "); KernelLog.String(currentAuthor); KernelLog.Ln;
ELSIF token = "PURPOSE" THEN
r.String(currentPurpose);
KernelLog.String("currentPurpose= "); KernelLog.String(currentPurpose); KernelLog.Ln;
END
END
END
END;
MakeRange(cur.pos.a, cur.pos.b, KindComment, NIL);
cur := cur.next
END
END NoteCommentRanges;
PROCEDURE CommentsFromNamedObject(no : TS.NamedObject);
BEGIN
NoteCommentRanges(no.preComment);
NoteCommentRanges(no.postComment);
END CommentsFromNamedObject;
PROCEDURE NoteDeclaration(no : TS.NamedObject);
BEGIN
MakeRange(no.pos.a, no.pos.b, KindDeclaration, no);
END NoteDeclaration;
PROCEDURE DumpM*(m : TS.Module);
BEGIN
CommentsFromNamedObject(m);
NoteDeclaration(m);
DumpDeclarations(m.scope);
IF m.scope.ownerBody # NIL THEN
DumpStatementSequence(m.scope.ownerBody, m.scope)
END
END DumpM;
PROCEDURE DumpLocalUses;
VAR i : LONGINT;
filename, scopePath, name, path : ARRAY 1024 OF CHAR;
a : ANY;
u : GlobalUse;
BEGIN
FOR i := 0 TO localUses.nof - 1 DO
ST.GetSourceReference(localUses.items[i], filename, scopePath);
a := globalUses.Find(scopePath);
IF a = NIL THEN
NEW(u);
globalUses.Add(scopePath, u);
ELSE
u := a(GlobalUse);
END;
u.AddFile(filename);
END;
END DumpLocalUses;
PROCEDURE GenerateModule(module : TS.Module; r : Streams.Reader; out : Streams.Writer);
VAR ch : CHAR;
w : Streams.Writer;
currentRange, pos, nextEnd : LONGINT;
inRange, inComment, lastInRange : BOOLEAN;
token : ARRAY 1024 OF CHAR;
filename, scopePath, name, path : ARRAY 1024 OF CHAR;
i : LONGINT;
referencedModule : TS.Module;
CONST DoXml = TRUE;
BEGIN
NEW(localUses);
IF ranges = NIL THEN NEW(ranges, 1000000)
ELSE
FOR i := 0 TO LEN(ranges) - 1 DO
ranges[i].kind := KindNoStart;
ranges[i].no := NIL
END
END;
DumpM(module);
IF out = NIL THEN
NEW(ml, module.name^, w);
ELSE
w := out;
END;
pos := 0;
inRange := FALSE; lastInRange := FALSE; inComment := FALSE;
IF DoXml THEN
w.String('<!DOCTYPE html>'); w.Ln;
w.String('<html>'); w.Ln();
w.String(' <head>'); w.Ln();
w.String(' <title>'); w.String(module.name^); w.String('</title>'); w.Ln();
w.String(' <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>'); w.Ln();
w.String(' <link rel="stylesheet" href="code.css" type="text/css" media="screen"/>'); w.Ln();
w.String(' <script src="highlight.js"> </script>'); w.Ln();
w.String(' </head>'); w.Ln();
w.String('<body onLoad="setup();">'); w.Ln();
w.String('<nav>'); w.Ln();
w.String(' <div class="menu">'); w.Ln();
w.String(' <ul>'); w.Ln();
w.String(' <li><a href="index.html">Index</a></li>'); w.Ln();
w.String(' </ul>'); w.Ln();
w.String(' </div>'); w.Ln();
w.String('</nav>'); w.Ln();
w.String('<div class="scroll"><code><pre>'); w.Ln();
END;
ch := r.Get();
REPEAT
IF ~inRange THEN
IF (ranges[pos].kind # KindNoStart) & (ranges[pos].b > pos) THEN
inRange := TRUE;
currentRange := pos;
nextEnd := ranges[pos].b;
CASE ranges[pos].kind OF
| KindComment :
w.String('<span class="comment">');
inComment := TRUE;
| KindDeclaration:
ST.GetSourceReference(ranges[pos].no, filename, scopePath);
Files.SplitPath(filename, path, name);
w.String('<a name="'); w.String(scopePath);w.String('">');
| KindUse :
scopePath := ""; filename := "";
IF ranges[pos].no.container # TFAOParser.Universe THEN
IF ranges[pos].no IS TS.Import THEN
referencedModule := TS.GetModule(ranges[pos].no(TS.Import));
IF referencedModule # NIL THEN
COPY(referencedModule.name^, scopePath);
IF referencedModule.filename # NIL THEN
COPY(referencedModule.filename^, filename)
END
END
ELSE
ST.GetSourceReference(ranges[pos].no, filename, scopePath);
END;
Files.SplitPath(filename, path, name);
w.String('<a href="'); w.String(name); w.String('.html#'); w.String(scopePath); w.String('">');
END
END
END
ELSE
IF pos = nextEnd THEN
IF token # "" THEN
w.String(token);
token := "";
END;
CASE ranges[currentRange].kind OF
| KindComment :
w.String('</span>');
| KindDeclaration:
w.String('</a>');
| KindUse:
IF ranges[currentRange].no.container # TFAOParser.Universe THEN
w.String('</a>');
END
END;
inRange := FALSE;
inComment := FALSE;
END
END;
IF ~inComment THEN
IF ~S.reservedChar[ORD(ch)] THEN
Strings.AppendChar(token, ch);
WHILE ~S.newChar[ORD(ch)] DO
ch := r.Get();
Strings.AppendChar(token, ch);
END
ELSE
IF IsKeyWord(token) THEN
w.String('<span class="keyword">');
w.String(token);
w.String('</span>');
ELSE
w.String(token);
END;
token := "";
IF ch = "<" THEN w.String("<")
ELSE w.Char(ch)
END;
WHILE ~S.newChar[ORD(ch)] DO
ch := r.Get();
w.Char(ch);
END
END
ELSE
IF ch = "<" THEN w.String("<")
ELSE w.Char(ch)
END;
WHILE ~S.newChar[ORD(ch)] DO
ch := r.Get();
w.Char(ch);
END
END;
INC(pos);
ch := r.Get();
UNTIL r.res # 0;
IF DoXml THEN
w.String('</pre></code>'); w.Ln();
w.String('<div class="footer">'); PageTime(w); w.String('</div>');
w.String("</div></body></html>"); w.Ln();
END;
w.Update;
DumpLocalUses;
END GenerateModule;
PROCEDURE InitWithText(t: Texts.Text; pos: LONGINT): Strings.String;
VAR buffer: Strings.String; len, i, j, ch: LONGINT; r: Texts.TextReader;
bytesPerChar: LONGINT;
PROCEDURE ExpandBuf(VAR oldBuf: Strings.String; newSize: LONGINT);
VAR newBuf: Strings.String; i: LONGINT;
BEGIN
IF LEN(oldBuf^) >= newSize THEN RETURN END;
NEW(newBuf, newSize);
FOR i := 0 TO LEN(oldBuf^)-1 DO
newBuf[i] := oldBuf[i];
END;
oldBuf := newBuf;
END ExpandBuf;
BEGIN
t.AcquireRead;
len := t.GetLength();
bytesPerChar := 2;
NEW(buffer, len * bytesPerChar);
NEW(r, t);
r.SetPosition(pos);
j := 0;
FOR i := 0 TO len-1 DO
r.ReadCh(ch);
WHILE ~UTF8Strings.EncodeChar(ch, buffer^, j) DO
INC(bytesPerChar);
ExpandBuf(buffer, bytesPerChar * len);
END;
END;
t.ReleaseRead;
RETURN buffer;
END InitWithText;
PROCEDURE ProcessFile(CONST filename, targetPath : ARRAY OF CHAR; indexFile : Streams.Writer);
VAR
module : TS.Module;
t : Texts.Text; res : LONGINT;
r : Streams.StringReader;
str : Strings.String;
name, path, targetFile : ARRAY 1024 OF CHAR;
f : Files.File;
fw : Files.Writer;
trap : BOOLEAN;
BEGIN
KernelLog.String("filename= "); KernelLog.String(filename); KernelLog.Ln;
NEW(t);
Files.SplitPath(filename, path, name);
Files.JoinPath(targetPath, name, targetFile);
Strings.Append(targetFile, ".html");
TFAOParser.ScanModule(filename, FALSE, module);
IF module # NIL THEN
module.filename := Strings.NewString(filename);
TextUtilities.LoadAuto(t, filename, res, res);
str := InitWithText(t, 0);
NEW(r, Strings.Length(str^));
r.Set(str^);
f := Files.New(targetFile);
Files.OpenWriter(fw, f, 0);
currentAuthor := "";
currentPurpose := "";
GenerateModule(module, r, fw);
IF (indexFile # NIL) THEN
indexFile.String('<tr><td><a href="'); indexFile.String(name); indexFile.String('.html">');
indexFile.String(module.name^); indexFile.String('</a></td><td>');
indexFile.String(currentPurpose); indexFile.String('</td><td>');
indexFile.String(currentAuthor); indexFile.String('</td></tr>');
indexFile.Ln
END;
fw.Update();
Files.Register(f)
END;
FINALLY
IF trap THEN
KernelLog.String("Parse error for "); KernelLog.String(filename); KernelLog.Ln;
END
END ProcessFile;
PROCEDURE Generate*(par : Commands.Context) ;
VAR
filename :ARRAY 256 OF CHAR;
sr : Streams.Reader;
t0, t1 : LONGINT;
module : TS.Module;
t : Texts.Text; res : LONGINT;
textReader : TextUtilities.TextReader;
BEGIN
NEW(globalUses);
sr := par.arg;
sr.String(filename);
KernelLog.String("Parsing "); KernelLog.String(filename);
t0 := Kernel.GetTicks();
NEW(t);
TFAOParser.ScanModule(filename, FALSE, module);
IF module # NIL THEN
module.filename := Strings.NewString(filename);
TextUtilities.LoadAuto(t, filename, res, res);
NEW(textReader, t);
GenerateModule(module, textReader, NIL);
TFDocGenerator.DocumentModule(module);
END;
t1 := Kernel.GetTicks();
KernelLog.String("t1-t0= "); KernelLog.Int(t1-t0, 0); KernelLog.Ln;
KernelLog.String(" done.");
END Generate;
PROCEDURE MakeXRef*(par : Commands.Context) ;
VAR e : Files.Enumerator;
path, name, exclude : ARRAY 256 OF CHAR; flags : SET; time, date, size : LONGINT;
sr : Streams.Reader;
indexFileWriter : Files.Writer;
f : Files.File;
BEGIN
NEW(globalUses);
sr := par.arg;
sr.String(path); sr.SkipWhitespace();
sr.String(exclude);
IF (path # "") & ~Strings.EndsWith("/", path) THEN Strings.Append(path, "/") END;
Strings.Append(path, "*.Mod");
KernelLog.String(path); KernelLog.Ln;
IF exclude # "" THEN
KernelLog.String("Excluding "); KernelLog.String(exclude); KernelLog.Ln;
END;
NEW(e);
e.Open(path, {});
KernelLog.String("Processing ... "); KernelLog.Ln;
f := Files.New("xref/index.html");
Files.OpenWriter(indexFileWriter, f, 0);
indexFileWriter.String("<html><table>"); indexFileWriter.Ln;
WHILE e.HasMoreEntries() DO
IF e.GetEntry(name, flags, time, date, size) THEN
IF (exclude = "") OR ~Strings.Match(exclude, name) THEN
ProcessFile(name, "xref", indexFileWriter);
ELSE
KernelLog.String("Excluding "); KernelLog.String(name); KernelLog.Ln;
END
END
END;
indexFileWriter.String("</table></html>"); indexFileWriter.Ln;
indexFileWriter.Update;
Files.Register(f)
END MakeXRef;
PROCEDURE PageTime(out : Streams.Writer);
VAR dateTimeStr : ARRAY 32 OF CHAR;
BEGIN
Strings.FormatDateTime("yyyy.mm.dd hh:nn:ss", Dates.Now(), dateTimeStr);
out.String(dateTimeStr)
END PageTime;
PROCEDURE IsKeyWord(CONST str : ARRAY OF CHAR) : BOOLEAN;
VAR s : LONGINT;
BEGIN
s := 0;
IF str = "ARRAY" THEN s := S.array
ELSIF str = "AWAIT" THEN s := S.passivate
ELSIF str = "BEGIN" THEN s := S.begin
ELSIF str = "BY" THEN s := S.by
ELSIF str = "CONST" THEN s := S.const
ELSIF str = "CASE" THEN s := S.case
ELSIF str = "CODE" THEN s := S.code
ELSIF str = "DO" THEN s := S.do
ELSIF str = "DIV" THEN s := S.div
ELSIF str = "DEFINITION" THEN s := S.definition
ELSIF str = "END" THEN s := S.end
ELSIF str = "ELSE" THEN s := S.else
ELSIF str = "ELSIF" THEN s := S.elsif
ELSIF str = "EXIT" THEN s := S.exit
ELSIF str = "FALSE" THEN s := S.false
ELSIF str = "FOR" THEN s := S.for
ELSIF str = "IF" THEN s := S.if
ELSIF str = "IN" THEN s := S.in
ELSIF str = "IS" THEN s := S.is
ELSIF str = "IMPORT" THEN s := S.import
ELSIF str = "IMPLEMENTS" THEN s := S.implements
ELSIF str = "LOOP" THEN s := S.loop
ELSIF str = "MOD" THEN s := S.mod
ELSIF str = "MODULE" THEN s := S.module
ELSIF str = "NIL" THEN s := S.nil
ELSIF str = "OR" THEN s := S.or
ELSIF str = "OF" THEN s := S.of
ELSIF str = "OBJECT" THEN s := S.object
ELSIF str = "PROCEDURE" THEN s := S.procedure
ELSIF str = "POINTER" THEN s := S.pointer
ELSIF str = "RECORD" THEN s := S.record
ELSIF str = "REPEAT" THEN s := S.repeat
ELSIF str = "RETURN" THEN s := S.return
ELSIF str = "REFINES" THEN s := S.refines
ELSIF str = "THEN" THEN s := S.then
ELSIF str = "TRUE" THEN s := S.true
ELSIF str = "TO" THEN s := S.to
ELSIF str = "TYPE" THEN s := S.type
ELSIF str = "UNTIL" THEN s := S.until
ELSIF str = "VAR" THEN s := S.var
ELSIF str = "WHILE" THEN s := S.while
ELSIF str = "WITH" THEN s := S.with
END;
RETURN s # 0
END IsKeyWord;
END TFXRef.
(* Make sure the TFPET symbol files are available (takes a few minutes) *)
TFAOParser.MakeSymbolFiles "D:\Aos\trunk\source\" "*Oberon*"~ (* d:/release/*.Mod *)
SystemTools.Free TFXRef TFDocGenerator~
TFXRef.MakeXRef "D:\Aos\trunk\source\" "*Oberon*"~
TFXRef.Generate HelloWorld.Mod ~
TFXRef.Generate I386.VMWareTools.Mod ~
TFXRef.Generate TFModuleTrees.Mod ~
TFXRef.Generate String.Mod ~