Browse Source

--- Merging r31366 into '.':
U utils/fpdoc/fpdocclasstree.pp
U utils/fpdoc/dglobals.pp
U utils/fpdoc/fpclasschart.pp
--- Recording mergeinfo for merge of r31366 into '.':
U .
--- Merging r31367 into '.':
G utils/fpdoc/fpclasschart.pp
G utils/fpdoc/fpdocclasstree.pp
--- Recording mergeinfo for merge of r31367 into '.':
G .
--- Merging r32183 into '.':
U packages/fcl-passrc/tests/tcstatements.pas
U packages/fcl-passrc/src/pparser.pp
--- Recording mergeinfo for merge of r32183 into '.':
G .
--- Merging r32360 into '.':
U utils/fpdoc/fpdoc.css
U utils/fpdoc/css.inc
--- Recording mergeinfo for merge of r32360 into '.':
G .
--- Merging r32374 into '.':
U utils/fpdoc/dw_html.pp
U utils/fpdoc/mkfpdoc.pp
G utils/fpdoc/dglobals.pp
U utils/fpdoc/fpdoc.pp
--- Recording mergeinfo for merge of r32374 into '.':
G .
--- Merging r32376 into '.':
A utils/fpdoc/examples
A utils/fpdoc/examples/gentest.sh
A utils/fpdoc/examples/project
A utils/fpdoc/examples/project/sample-project.xml
A utils/fpdoc/examples/project/readme.txt
A utils/fpdoc/examples/basedir
A utils/fpdoc/examples/basedir/readme.txt
A utils/fpdoc/examples/basedir/sample-project.xml
A utils/fpdoc/examples/simple
A utils/fpdoc/examples/simple/testunit.xml
A utils/fpdoc/examples/simple/html.bat
A utils/fpdoc/examples/simple/readme.txt
A utils/fpdoc/examples/simple/html.sh
A utils/fpdoc/examples/simple/testunit.pp
D utils/fpdoc/testunit.pp
D utils/fpdoc/testunit.xml
D utils/fpdoc/gentest.sh
--- Recording mergeinfo for merge of r32376 into '.':
G .
--- Recording mergeinfo for merge of r34113 into '.':
G .
--- Merging r34114 into '.':
U packages/fcl-passrc/src/pscanner.pp
--- Recording mergeinfo for merge of r34114 into '.':
G .
--- Merging r34132 into '.':
G packages/fcl-passrc/src/pscanner.pp
G packages/fcl-passrc/src/pparser.pp
--- Recording mergeinfo for merge of r34132 into '.':
G .
--- Merging r34169 into '.':
G packages/fcl-passrc/src/pscanner.pp
G packages/fcl-passrc/src/pparser.pp
--- Recording mergeinfo for merge of r34169 into '.':
G .

# revisions: 31366,31367,32183,32360,32374,32376,34113,34114,34132,34169

git-svn-id: branches/fixes_3_0@35971 -

marco 8 years ago
parent
commit
3803b78124

+ 10 - 3
.gitattributes

@@ -15449,6 +15449,16 @@ utils/fpdoc/dw_txt.pp svneol=native#text/plain
 utils/fpdoc/dw_xml.pp svneol=native#text/plain
 utils/fpdoc/dw_xml.pp svneol=native#text/plain
 utils/fpdoc/dwlinear.pp svneol=native#text/plain
 utils/fpdoc/dwlinear.pp svneol=native#text/plain
 utils/fpdoc/dwriter.pp svneol=native#text/plain
 utils/fpdoc/dwriter.pp svneol=native#text/plain
+utils/fpdoc/examples/basedir/readme.txt svneol=native#text/plain
+utils/fpdoc/examples/basedir/sample-project.xml svneol=native#text/plain
+utils/fpdoc/examples/gentest.sh svneol=native#text/plain
+utils/fpdoc/examples/project/readme.txt svneol=native#text/plain
+utils/fpdoc/examples/project/sample-project.xml svneol=native#text/plain
+utils/fpdoc/examples/simple/html.bat svneol=native#text/plain
+utils/fpdoc/examples/simple/html.sh svneol=native#text/plain
+utils/fpdoc/examples/simple/readme.txt svneol=native#text/plain
+utils/fpdoc/examples/simple/testunit.pp svneol=native#text/plain
+utils/fpdoc/examples/simple/testunit.xml svneol=native#text/plain
 utils/fpdoc/fpclasschart.lpi svneol=native#text/plain
 utils/fpdoc/fpclasschart.lpi svneol=native#text/plain
 utils/fpdoc/fpclasschart.pp svneol=native#text/plain
 utils/fpdoc/fpclasschart.pp svneol=native#text/plain
 utils/fpdoc/fpde/Makefile svneol=native#text/plain
 utils/fpdoc/fpde/Makefile svneol=native#text/plain
@@ -15490,7 +15500,6 @@ utils/fpdoc/fpdocstripper.lpi svneol=native#text/plain
 utils/fpdoc/fpdocstripper.pp svneol=native#text/plain
 utils/fpdoc/fpdocstripper.pp svneol=native#text/plain
 utils/fpdoc/fpdocxmlopts.pas svneol=native#text/plain
 utils/fpdoc/fpdocxmlopts.pas svneol=native#text/plain
 utils/fpdoc/fpmake.pp svneol=native#text/plain
 utils/fpdoc/fpmake.pp svneol=native#text/plain
-utils/fpdoc/gentest.sh svneol=native#text/plain
 utils/fpdoc/images/minus.png -text svneol=unset#image/png
 utils/fpdoc/images/minus.png -text svneol=unset#image/png
 utils/fpdoc/images/plus.png -text svneol=unset#image/png
 utils/fpdoc/images/plus.png -text svneol=unset#image/png
 utils/fpdoc/intl/Makefile svneol=native#text/plain
 utils/fpdoc/intl/Makefile svneol=native#text/plain
@@ -15511,8 +15520,6 @@ utils/fpdoc/mkfpdocproj.pp svneol=native#text/plain
 utils/fpdoc/plusimage.inc svneol=native#text/plain
 utils/fpdoc/plusimage.inc svneol=native#text/plain
 utils/fpdoc/sample-project.xml svneol=native#text/plain
 utils/fpdoc/sample-project.xml svneol=native#text/plain
 utils/fpdoc/sh_pas.pp svneol=native#text/plain
 utils/fpdoc/sh_pas.pp svneol=native#text/plain
-utils/fpdoc/testunit.pp svneol=native#text/plain
-utils/fpdoc/testunit.xml svneol=native#text/plain
 utils/fpdoc/unitdiff.pp svneol=native#text/plain
 utils/fpdoc/unitdiff.pp svneol=native#text/plain
 utils/fpgmake/fpgmake.pp svneol=native#text/plain
 utils/fpgmake/fpgmake.pp svneol=native#text/plain
 utils/fpgmake/fpmake.cft svneol=native#text/plain
 utils/fpgmake/fpmake.cft svneol=native#text/plain

+ 236 - 120
packages/fcl-passrc/src/pparser.pp

@@ -23,6 +23,53 @@ interface
 
 
 uses SysUtils, Classes, PasTree, PScanner;
 uses SysUtils, Classes, PasTree, PScanner;
 
 
+// message numbers
+const
+  nErrNoSourceGiven = 2001;
+  nErrMultipleSourceFiles = 2002;
+  nParserError = 2003;
+  nParserErrorAtToken = 2004;
+  nParserUngetTokenError = 2005;
+  nParserExpectTokenError = 2006;
+  nParserForwardNotInterface = 2007;
+  nParserExpectVisibility = 2008;
+  nParserStrangeVisibility = 2009;
+  nParserExpectToken2Error = 2010;
+  nParserExpectedCommaRBracket = 2011;
+  nParserExpectedCommaSemicolon = 2012;
+  nParserExpectedAssignIn = 2013;
+  nParserExpectedCommaColon = 2014;
+  nErrUnknownOperatorType = 2015;
+  nParserOnlyOneArgumentCanHaveDefault = 2016;
+  nParserExpectedLBracketColon = 2017;
+  nParserExpectedSemiColonEnd = 2018;
+  nParserExpectedConstVarID = 2019;
+  nParserExpectedNested = 2020;
+  nParserExpectedColonID = 2021;
+  nParserSyntaxError = 2022;
+  nParserTypeSyntaxError = 2023;
+  nParserArrayTypeSyntaxError = 2024;
+  nParserExpectedIdentifier = 2026;
+  nParserNotAProcToken = 2026;
+  nRangeExpressionExpected = 2027;
+  nParserExpectCase = 2028;
+  nParserHelperNotAllowed = 2029;
+  nLogStartImplementation = 2030;
+  nLogStartInterface = 2031;
+  nParserNoConstructorAllowed = 2032;
+  nParserNoFieldsAllowed = 2033;
+  nParserInvalidRecordVisibility = 2034;
+  nErrRecordConstantsNotAllowed = 2035;
+  nErrRecordMethodsNotAllowed = 2036;
+  nErrRecordPropertiesNotAllowed = 2037;
+  nErrRecordVisibilityNotAllowed = 2038;
+  nParserTypeNotAllowedHere = 2039;
+  nParserNotAnOperand = 2040;
+  nParserArrayPropertiesCannotHaveDefaultValue = 2041;
+  nParserDefaultPropertyMustBeArray = 2042;
+  nParserUnknownProcedureType = 2043;
+
+// resourcestring patterns of messages
 resourcestring
 resourcestring
   SErrNoSourceGiven = 'No source file specified';
   SErrNoSourceGiven = 'No source file specified';
   SErrMultipleSourceFiles = 'Please specify only one source file';
   SErrMultipleSourceFiles = 'Please specify only one source file';
@@ -62,6 +109,11 @@ resourcestring
   SErrRecordMethodsNotAllowed = 'Record methods not allowed at this location.';
   SErrRecordMethodsNotAllowed = 'Record methods not allowed at this location.';
   SErrRecordPropertiesNotAllowed = 'Record properties not allowed at this location.';
   SErrRecordPropertiesNotAllowed = 'Record properties not allowed at this location.';
   SErrRecordVisibilityNotAllowed = 'Record visibilities not allowed at this location.';
   SErrRecordVisibilityNotAllowed = 'Record visibilities not allowed at this location.';
+  SParserTypeNotAllowedHere = 'Type "%s" not allowed here';
+  SParserNotAnOperand = 'Not an operand: (%d : %s)';
+  SParserArrayPropertiesCannotHaveDefaultValue = 'Array properties cannot have default value';
+  SParserDefaultPropertyMustBeArray = 'The default property must be an array property';
+  SParserUnknownProcedureType = 'Unknown procedure type "%d"';
 
 
 type
 type
   TPasParserLogHandler = Procedure (Sender : TObject; Const Msg : String) of object;
   TPasParserLogHandler = Procedure (Sender : TObject; Const Msg : String) of object;
@@ -128,6 +180,12 @@ type
   private
   private
     FCurModule: TPasModule;
     FCurModule: TPasModule;
     FFileResolver: TBaseFileResolver;
     FFileResolver: TBaseFileResolver;
+    FImplicitUses: TStrings;
+    FLastMsg: string;
+    FLastMsgArgs: TMessageArgs;
+    FLastMsgNumber: integer;
+    FLastMsgPattern: string;
+    FLastMsgType: TMessageType;
     FLogEvents: TPParserLogEvents;
     FLogEvents: TPParserLogEvents;
     FOnLog: TPasParserLogHandler;
     FOnLog: TPasParserLogHandler;
     FOptions: TPOptions;
     FOptions: TPOptions;
@@ -158,8 +216,8 @@ type
     Function SaveComments : String;
     Function SaveComments : String;
     Function SaveComments(Const AValue : String) : String;
     Function SaveComments(Const AValue : String) : String;
     function LogEvent(E : TPParserLogEvent) : Boolean; inline;
     function LogEvent(E : TPParserLogEvent) : Boolean; inline;
-    Procedure DoLog(Const Msg : String; SkipSourceInfo : Boolean = False);overload;
-    Procedure DoLog(Const Fmt : String; Args : Array of const;SkipSourceInfo : Boolean = False);overload;
+    Procedure DoLog(MsgType: TMessageType; MsgNumber: integer; Const Msg : String; SkipSourceInfo : Boolean = False);overload;
+    Procedure DoLog(MsgType: TMessageType; MsgNumber: integer; Const Fmt : String; Args : Array of const;SkipSourceInfo : Boolean = False);overload;
     function GetProcTypeFromToken(tk: TToken; IsClass: Boolean=False ): TProcType;
     function GetProcTypeFromToken(tk: TToken; IsClass: Boolean=False ): TProcType;
     procedure ParseRecordFieldList(ARec: TPasRecordType; AEndToken: TToken; AllowMethods : Boolean);
     procedure ParseRecordFieldList(ARec: TPasRecordType; AEndToken: TToken; AllowMethods : Boolean);
     procedure ParseRecordVariantParts(ARec: TPasRecordType; AEndToken: TToken);
     procedure ParseRecordVariantParts(ARec: TPasRecordType; AEndToken: TToken);
@@ -170,8 +228,11 @@ type
     procedure ReadGenericArguments(List : TFPList;Parent : TPasElement);
     procedure ReadGenericArguments(List : TFPList;Parent : TPasElement);
     function CheckProcedureArgs(Parent: TPasElement; Args: TFPList; Mandatory: Boolean): boolean;
     function CheckProcedureArgs(Parent: TPasElement; Args: TFPList; Mandatory: Boolean): boolean;
     function CheckVisibility(S: String; var AVisibility: TPasMemberVisibility): Boolean;
     function CheckVisibility(S: String; var AVisibility: TPasMemberVisibility): Boolean;
-    procedure ParseExc(const Msg: String);
-    procedure ParseExc(const Fmt: String; Args : Array of const);
+    procedure ParseExc(MsgNumber: integer; const Msg: String);
+    procedure ParseExc(MsgNumber: integer; const Fmt: String; Args : Array of const);
+    procedure ParseExcExpectedIdentifier;
+    procedure ParseExcSyntaxError;
+    procedure ParseExcTokenError(const Arg: string);
     function OpLevel(t: TToken): Integer;
     function OpLevel(t: TToken): Integer;
     Function TokenToExprOp (AToken : TToken) : TExprOpCode;
     Function TokenToExprOp (AToken : TToken) : TExprOpCode;
     function CreateElement(AClass: TPTreeElement; const AName: String; AParent: TPasElement): TPasElement;overload;
     function CreateElement(AClass: TPTreeElement; const AName: String; AParent: TPasElement): TPasElement;overload;
@@ -195,6 +256,7 @@ type
   public
   public
     constructor Create(AScanner: TPascalScanner; AFileResolver: TBaseFileResolver;  AEngine: TPasTreeContainer);
     constructor Create(AScanner: TPascalScanner; AFileResolver: TBaseFileResolver;  AEngine: TPasTreeContainer);
     Destructor Destroy; override;
     Destructor Destroy; override;
+    procedure SetLastMsg(MsgType: TMessageType; MsgNumber: integer; Const Fmt : String; Args : Array of const);
     // General parsing routines
     // General parsing routines
     function CurTokenName: String;
     function CurTokenName: String;
     function CurTokenText: String;
     function CurTokenText: String;
@@ -262,6 +324,12 @@ type
     Property CurModule : TPasModule Read FCurModule;
     Property CurModule : TPasModule Read FCurModule;
     Property LogEvents : TPParserLogEvents Read FLogEvents Write FLogEvents;
     Property LogEvents : TPParserLogEvents Read FLogEvents Write FLogEvents;
     Property OnLog : TPasParserLogHandler Read FOnLog Write FOnLog;
     Property OnLog : TPasParserLogHandler Read FOnLog Write FOnLog;
+    property ImplicitUses: TStrings read FImplicitUses;
+    property LastMsg: string read FLastMsg write FLastMsg;
+    property LastMsgNumber: integer read FLastMsgNumber write FLastMsgNumber;
+    property LastMsgType: TMessageType read FLastMsgType write FLastMsgType;
+    property LastMsgPattern: string read FLastMsgPattern write FLastMsgPattern;
+    property LastMsgArgs: TMessageArgs read FLastMsgArgs write FLastMsgArgs;
   end;
   end;
 
 
 function ParseSource(AEngine: TPasTreeContainer;
 function ParseSource(AEngine: TPasTreeContainer;
@@ -546,16 +614,34 @@ end;
   TPasParser
   TPasParser
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}
 
 
-procedure TPasParser.ParseExc(const Msg: String);
+procedure TPasParser.ParseExc(MsgNumber: integer; const Msg: String);
 begin
 begin
-  raise EParserError.Create(Format(SParserErrorAtToken, [Msg, CurTokenName, Scanner.CurFilename, Scanner.CurRow, Scanner.CurColumn])
+  ParseExc(MsgNumber,Msg,[]);
+end;
+
+procedure TPasParser.ParseExc(MsgNumber: integer; const Fmt: String;
+  Args: array of const);
+begin
+  SetLastMsg(mtError,MsgNumber,Fmt,Args);
+  raise EParserError.Create(Format(SParserErrorAtToken,
+    [FLastMsg, CurTokenName, Scanner.CurFilename, Scanner.CurRow, Scanner.CurColumn])
     {$ifdef addlocation}+' ('+inttostr(scanner.currow)+' '+inttostr(scanner.curcolumn)+')'{$endif},
     {$ifdef addlocation}+' ('+inttostr(scanner.currow)+' '+inttostr(scanner.curcolumn)+')'{$endif},
     Scanner.CurFilename, Scanner.CurRow, Scanner.CurColumn);
     Scanner.CurFilename, Scanner.CurRow, Scanner.CurColumn);
 end;
 end;
 
 
-procedure TPasParser.ParseExc(const Fmt: String; Args: array of const);
+procedure TPasParser.ParseExcExpectedIdentifier;
 begin
 begin
-  ParseExc(Format(Fmt,Args));
+  ParseExc(nParserExpectedIdentifier,SParserExpectedIdentifier);
+end;
+
+procedure TPasParser.ParseExcSyntaxError;
+begin
+  ParseExc(nParserSyntaxError,SParserSyntaxError);
+end;
+
+procedure TPasParser.ParseExcTokenError(const Arg: string);
+begin
+  ParseExc(nParserExpectTokenError,SParserExpectTokenError,[Arg]);
 end;
 end;
 
 
 constructor TPasParser.Create(AScanner: TPascalScanner;
 constructor TPasParser.Create(AScanner: TPascalScanner;
@@ -573,10 +659,13 @@ begin
     If FEngine.NeedComments then
     If FEngine.NeedComments then
       FScanner.SkipComments:=Not FEngine.NeedComments;
       FScanner.SkipComments:=Not FEngine.NeedComments;
     end;
     end;
+  FImplicitUses := TStringList.Create;
+  FImplicitUses.Add('System'); // system always implicitely first.
 end;
 end;
 
 
 destructor TPasParser.Destroy;
 destructor TPasParser.Destroy;
 begin
 begin
+  FreeAndNil(FImplicitUses);
   FreeAndNil(FCommentsBuffer[0]);
   FreeAndNil(FCommentsBuffer[0]);
   FreeAndNil(FCommentsBuffer[1]);
   FreeAndNil(FCommentsBuffer[1]);
   if Assigned(FEngine) then
   if Assigned(FEngine) then
@@ -666,7 +755,7 @@ procedure TPasParser.UngetToken;
 
 
 begin
 begin
   if FTokenBufferIndex = 0 then
   if FTokenBufferIndex = 0 then
-    ParseExc(SParserUngetTokenError)
+    ParseExc(nParserUngetTokenError,SParserUngetTokenError)
   else begin
   else begin
     Dec(FTokenBufferIndex);
     Dec(FTokenBufferIndex);
     if FTokenBufferIndex>0 then
     if FTokenBufferIndex>0 then
@@ -686,7 +775,7 @@ end;
 procedure TPasParser.CheckToken(tk: TToken);
 procedure TPasParser.CheckToken(tk: TToken);
 begin
 begin
   if (CurToken<>tk) then
   if (CurToken<>tk) then
-    ParseExc(Format(SParserExpectTokenError, [TokenInfos[tk]]));
+    ParseExcTokenError(TokenInfos[tk]);
 end;
 end;
 
 
 
 
@@ -789,7 +878,7 @@ begin
      begin
      begin
      NextToken;
      NextToken;
      if Not (CurToken in [tkArray, tkRecord, tkObject, tkClass]) then
      if Not (CurToken in [tkArray, tkRecord, tkObject, tkClass]) then
-       ParseExc(Format(SParserExpectTokenError,['ARRAY, RECORD, OBJECT or CLASS']))
+       ParseExcTokenError('ARRAY, RECORD, OBJECT or CLASS');
      end;
      end;
 end;
 end;
 
 
@@ -976,10 +1065,10 @@ begin
         if CurToken = tkBraceClose then
         if CurToken = tkBraceClose then
           Break
           Break
         else if not (CurToken=tkComma) then
         else if not (CurToken=tkComma) then
-          ParseExc(SParserExpectedCommaRBracket);
+          ParseExc(nParserExpectedCommaRBracket,SParserExpectedCommaRBracket);
         end
         end
       else if not (CurToken=tkComma) then
       else if not (CurToken=tkComma) then
-        ParseExc(SParserExpectedCommaRBracket)
+        ParseExc(nParserExpectedCommaRBracket,SParserExpectedCommaRBracket)
       end;
       end;
   except
   except
     FreeAndNil(Result);
     FreeAndNil(Result);
@@ -1021,7 +1110,7 @@ begin
     begin
     begin
     CH:=False;
     CH:=False;
     if (CurToken in FullTypeTokens) then
     if (CurToken in FullTypeTokens) then
-      ParseExc('Type '+CurtokenText+' not allowed here');
+      ParseExc(nParserTypeNotAllowedHere,SParserTypeNotAllowedHere,[CurtokenText]);
     end;
     end;
   Try
   Try
     case CurToken of
     case CurToken of
@@ -1125,7 +1214,7 @@ begin
           end
           end
         end
         end
       else
       else
-        ParseExc(SParserArrayTypeSyntaxError);
+        ParseExc(nParserArrayTypeSyntaxError,SParserArrayTypeSyntaxError);
     end;
     end;
   except
   except
     FreeAndNil(Result);
     FreeAndNil(Result);
@@ -1231,7 +1320,7 @@ begin
     tkDot                   : Result:=eopSubIdent;
     tkDot                   : Result:=eopSubIdent;
     tkCaret                 : Result:=eopDeref;
     tkCaret                 : Result:=eopDeref;
   else
   else
-    ParseExc(format('Not an operand: (%d : %s)',[AToken,TokenInfos[AToken]]));
+    ParseExc(nParserNotAnOperand,SParserNotAnOperand,[AToken,TokenInfos[AToken]]);
   end;
   end;
 end;
 end;
  
  
@@ -1294,7 +1383,7 @@ begin
       NextToken;
       NextToken;
       if (length(CurTokenText)=0) or not (CurTokenText[1] in ['A'..'_']) then begin
       if (length(CurTokenText)=0) or not (CurTokenText[1] in ['A'..'_']) then begin
         UngetToken;
         UngetToken;
-        ParseExc(SParserExpectedIdentifier);
+        ParseExcExpectedIdentifier;
       end;
       end;
       x:=TPrimitiveExpr.Create(AParent,pekString, '@'+CurTokenText);
       x:=TPrimitiveExpr.Create(AParent,pekString, '@'+CurTokenText);
     end;
     end;
@@ -1303,12 +1392,12 @@ begin
       NextToken;
       NextToken;
       if not (length(CurTokenText)=1) or not (CurTokenText[1] in ['A'..'_']) then begin
       if not (length(CurTokenText)=1) or not (CurTokenText[1] in ['A'..'_']) then begin
         UngetToken;
         UngetToken;
-        ParseExc(SParserExpectedIdentifier);
+        ParseExcExpectedIdentifier;
       end;
       end;
       x:=TPrimitiveExpr.Create(AParent,pekString, '^'+CurTokenText);
       x:=TPrimitiveExpr.Create(AParent,pekString, '^'+CurTokenText);
     end;
     end;
   else
   else
-    ParseExc(SParserExpectedIdentifier);
+    ParseExcExpectedIdentifier;
   end;
   end;
 
 
   if x.Kind<>pekSet then NextToken;
   if x.Kind<>pekSet then NextToken;
@@ -1327,7 +1416,7 @@ begin
         else
         else
           begin
           begin
           UngetToken;
           UngetToken;
-          ParseExc(SParserExpectedIdentifier);
+          ParseExcExpectedIdentifier;
           end;
           end;
         x:=b;
         x:=b;
         end;
         end;
@@ -1547,7 +1636,7 @@ begin
      // Writeln('Bin ',NotBinary ,' or EOE ',isEndOfExp, ' Ex ',Assigned(x),' stack ',ExpStack.Count);
      // Writeln('Bin ',NotBinary ,' or EOE ',isEndOfExp, ' Ex ',Assigned(x),' stack ',ExpStack.Count);
     until NotBinary or isEndOfExp;
     until NotBinary or isEndOfExp;
 
 
-    if not NotBinary then ParseExc(SParserExpectedIdentifier);
+    if not NotBinary then ParseExcExpectedIdentifier;
 
 
     while opstack.Count>0 do PopAndPushOperator;
     while opstack.Count>0 do PopAndPushOperator;
 
 
@@ -1639,13 +1728,13 @@ begin
     else
     else
       // Binary expression!  ((128 div sizeof(longint)) - 3);       ;
       // Binary expression!  ((128 div sizeof(longint)) - 3);       ;
       Result:=DoParseExpression(AParent,x);
       Result:=DoParseExpression(AParent,x);
-      if CurToken<>tkBraceClose then ParseExc(SParserExpectedCommaRBracket);
+      if CurToken<>tkBraceClose then ParseExc(nParserExpectedCommaRBracket,SParserExpectedCommaRBracket);
       NextToken;
       NextToken;
       if CurToken <> tkSemicolon then // the continue of expresion
       if CurToken <> tkSemicolon then // the continue of expresion
         Result:=DoParseExpression(AParent,Result);
         Result:=DoParseExpression(AParent,Result);
       Exit;
       Exit;
     end;
     end;
-    if CurToken<>tkBraceClose then ParseExc(SParserExpectedCommaRBracket);
+    if CurToken<>tkBraceClose then ParseExc(nParserExpectedCommaRBracket,SParserExpectedCommaRBracket);
     NextToken;
     NextToken;
   end;
   end;
 end;
 end;
@@ -1745,7 +1834,7 @@ begin
   else
   else
     ungettoken;
     ungettoken;
     ParseProgram(Module,True);
     ParseProgram(Module,True);
-  //    ParseExc(Format(SParserExpectTokenError, ['unit']));
+  //    ParseExcTokenError('unit');
   end;
   end;
 end;
 end;
 
 
@@ -1777,7 +1866,7 @@ begin
 //    ExpectToken(tkSemicolon);
 //    ExpectToken(tkSemicolon);
     ExpectToken(tkInterface);
     ExpectToken(tkInterface);
     If LogEvent(pleInterface) then
     If LogEvent(pleInterface) then
-      DoLog(SLogStartInterface );
+      DoLog(mtInfo,nLogStartInterface,SLogStartInterface);
     ParseInterface;
     ParseInterface;
   finally
   finally
     FCurModule:=nil;
     FCurModule:=nil;
@@ -1815,14 +1904,14 @@ begin
         PP.InputFile:=ExpectIdentifier;
         PP.InputFile:=ExpectIdentifier;
         NextToken;
         NextToken;
         if Not (CurToken in [tkBraceClose,tkComma]) then
         if Not (CurToken in [tkBraceClose,tkComma]) then
-          ParseExc(SParserExpectedCommaRBracket);
+          ParseExc(nParserExpectedCommaRBracket,SParserExpectedCommaRBracket);
         If (CurToken=tkComma) then
         If (CurToken=tkComma) then
           PP.OutPutFile:=ExpectIdentifier;
           PP.OutPutFile:=ExpectIdentifier;
         ExpectToken(tkBraceClose);
         ExpectToken(tkBraceClose);
         NextToken;
         NextToken;
         end;
         end;
       if (CurToken<>tkSemicolon) then
       if (CurToken<>tkSemicolon) then
-        ParseExc(Format(SParserExpectTokenError,[';']));
+        ParseExcTokenError(';');
       end;
       end;
     Section := TProgramSection(CreateElement(TProgramSection, '', CurModule));
     Section := TProgramSection(CreateElement(TProgramSection, '', CurModule));
     PP.ProgramSection := Section;
     PP.ProgramSection := Section;
@@ -1850,7 +1939,7 @@ begin
     end;
     end;
     NextToken;
     NextToken;
     if (CurToken<>tkSemicolon) then
     if (CurToken<>tkSemicolon) then
-        ParseExc(Format(SParserExpectTokenError,[';']));
+        ParseExcTokenError(';');
     Section := TLibrarySection(CreateElement(TLibrarySection, '', CurModule));
     Section := TLibrarySection(CreateElement(TLibrarySection, '', CurModule));
     PP.LibrarySection := Section;
     PP.LibrarySection := Section;
     ParseDeclarations(Section);
     ParseDeclarations(Section);
@@ -1964,7 +2053,7 @@ begin
       else
       else
         Result:=ptOperator;
         Result:=ptOperator;
   else
   else
-    ParseExc(SParserNotAProcToken);
+    ParseExc(nParserNotAProcToken,SParserNotAProcToken);
   end;
   end;
 end;
 end;
 
 
@@ -1993,7 +2082,7 @@ begin
       tkend:
       tkend:
         begin
         begin
         If (CurModule is TPasProgram) and (CurModule.InitializationSection=Nil) then
         If (CurModule is TPasProgram) and (CurModule.InitializationSection=Nil) then
-          ParseExc(Format(SParserExpectTokenError,['begin']));
+          ParseExcTokenError('begin');
         ExpectToken(tkDot);
         ExpectToken(tkDot);
         break;
         break;
         end;
         end;
@@ -2003,7 +2092,7 @@ begin
           If Not Engine.InterfaceOnly then
           If Not Engine.InterfaceOnly then
             begin
             begin
             If LogEvent(pleImplementation) then
             If LogEvent(pleImplementation) then
-              DoLog(SLogStartImplementation);
+              DoLog(mtInfo,nLogStartImplementation,SLogStartImplementation);
             ParseImplementation;
             ParseImplementation;
             end;
             end;
           break;
           break;
@@ -2026,7 +2115,7 @@ begin
         if Declarations is TPasSection then
         if Declarations is TPasSection then
           ParseUsesList(TPasSection(Declarations))
           ParseUsesList(TPasSection(Declarations))
         else
         else
-          ParseExc(SParserSyntaxError);
+          ParseExcSyntaxError;
       tkConst:
       tkConst:
         CurBlock := declConst;
         CurBlock := declConst;
       tkexports:
       tkexports:
@@ -2158,13 +2247,13 @@ begin
               Declarations.properties.add(PropEl);
               Declarations.properties.add(PropEl);
               end;
               end;
           else
           else
-            ParseExc(SParserSyntaxError);
+            ParseExcSyntaxError;
           end;
           end;
         end;
         end;
       tkGeneric:
       tkGeneric:
         begin
         begin
           if CurBlock <> declType then
           if CurBlock <> declType then
-            ParseExc(SParserSyntaxError);
+            ParseExcSyntaxError;
           TypeName := ExpectIdentifier;
           TypeName := ExpectIdentifier;
           ClassEl := TPasClassType(Engine.CreateElement(TPasClassType,TypeName,Declarations, Scanner.CurFilename, Scanner.CurRow));
           ClassEl := TPasClassType(Engine.CreateElement(TPasClassType,TypeName,Declarations, Scanner.CurFilename, Scanner.CurRow));
           ClassEl.ObjKind:=okGeneric;
           ClassEl.ObjKind:=okGeneric;
@@ -2196,7 +2285,7 @@ begin
           break;
           break;
           end
           end
         else
         else
-          ParseExc(SParserSyntaxError);
+          ParseExcSyntaxError;
         end;
         end;
       tklabel:
       tklabel:
         begin
         begin
@@ -2204,7 +2293,7 @@ begin
             ParseLabels(Declarations);
             ParseLabels(Declarations);
         end;
         end;
     else
     else
-      ParseExc(SParserSyntaxError);
+      ParseExcSyntaxError;
     end;
     end;
   end;
   end;
 end;
 end;
@@ -2226,9 +2315,15 @@ procedure TPasParser.ParseUsesList(ASection: TPasSection);
 var
 var
   AUnitName: String;
   AUnitName: String;
   Element: TPasElement;
   Element: TPasElement;
+  i: Integer;
 begin
 begin
   If not (Asection.ClassType=TImplementationSection) Then // interface,program,library,package
   If not (Asection.ClassType=TImplementationSection) Then // interface,program,library,package
-    Element:=CheckUnit('System'); // system always implicitely first.    
+    begin
+    // load implicit units, like 'System'
+    for i:=0 to ImplicitUses.Count-1 do
+      CheckUnit(ImplicitUses[i]);
+    end;
+
   Repeat
   Repeat
     AUnitName := ExpectIdentifier; 
     AUnitName := ExpectIdentifier; 
     NextToken;
     NextToken;
@@ -2250,7 +2345,7 @@ begin
       end;
       end;
 
 
     if Not (CurToken in [tkComma,tkSemicolon]) then
     if Not (CurToken in [tkComma,tkSemicolon]) then
-      ParseExc(SParserExpectedCommaSemicolon);
+      ParseExc(nParserExpectedCommaSemicolon,SParserExpectedCommaSemicolon);
   Until (CurToken=tkSemicolon);
   Until (CurToken=tkSemicolon);
 end;
 end;
 
 
@@ -2305,8 +2400,8 @@ begin
     List.Add(CreateElement(TPasGenericTemplateType,N,Parent));
     List.Add(CreateElement(TPasGenericTemplateType,N,Parent));
     NextToken;
     NextToken;
     if not (CurToken in [tkComma, tkGreaterThan]) then
     if not (CurToken in [tkComma, tkGreaterThan]) then
-      ParseExc(Format(SParserExpectToken2Error,
-        [TokenInfos[tkComma], TokenInfos[tkGreaterThan]]));
+      ParseExc(nParserExpectToken2Error,SParserExpectToken2Error,
+        [TokenInfos[tkComma], TokenInfos[tkGreaterThan]]);
   until CurToken = tkGreaterThan;
   until CurToken = tkGreaterThan;
 end;
 end;
 
 
@@ -2323,14 +2418,14 @@ begin
     if Full then
     if Full then
       begin
       begin
       If not (CurToken=tkEqual) then
       If not (CurToken=tkEqual) then
-        ParseExc(Format(SParserExpectTokenError,[TokenInfos[tkEqual]]));
+        ParseExcTokenError(TokenInfos[tkEqual]);
       end;
       end;
     NextToken;
     NextToken;
     PE:=DoParseExpression(Result,Nil);
     PE:=DoParseExpression(Result,Nil);
     if not ((PE is TBinaryExpr) and (TBinaryExpr(PE).Kind=pekRange)) then
     if not ((PE is TBinaryExpr) and (TBinaryExpr(PE).Kind=pekRange)) then
       begin
       begin
       FreeAndNil(PE);
       FreeAndNil(PE);
-      ParseExc(SRangeExpressionExpected);
+      ParseExc(nRangeExpressionExpected,SRangeExpressionExpected);
       end;
       end;
     Result.RangeExpr:=PE as TBinaryExpr;
     Result.RangeExpr:=PE as TBinaryExpr;
     UngetToken;
     UngetToken;
@@ -2362,7 +2457,7 @@ begin
       E.ExportName:=DoParseExpression(E,Nil)
       E.ExportName:=DoParseExpression(E,Nil)
       end;
       end;
     if not (CurToken in [tkComma,tkSemicolon]) then
     if not (CurToken in [tkComma,tkSemicolon]) then
-      ParseExc(SParserExpectedCommaSemicolon);
+      ParseExc(nParserExpectedCommaSemicolon,SParserExpectedCommaSemicolon);
   until (CurToken=tkSemicolon);
   until (CurToken=tkSemicolon);
 end;
 end;
 
 
@@ -2488,12 +2583,12 @@ begin
         if (CurToken in [tkString,tkIdentifier]) then
         if (CurToken in [tkString,tkIdentifier]) then
           Result := Result + CurTokenText
           Result := Result + CurTokenText
         else
         else
-          ParseExc(SParserSyntaxError);
+          ParseExcSyntaxError;
         ExportName:=CurTokenText;
         ExportName:=CurTokenText;
         NextToken;
         NextToken;
         end
         end
       else
       else
-        ParseExc(SParserSyntaxError);
+        ParseExcSyntaxError;
       end;
       end;
     end;
     end;
 end;
 end;
@@ -2520,7 +2615,7 @@ begin
       VarNames.Add(CurTokenString);
       VarNames.Add(CurTokenString);
       NextToken;
       NextToken;
       if Not (CurToken in [tkComma,tkColon]) then
       if Not (CurToken in [tkComma,tkColon]) then
-        ParseExc(SParserExpectedCommaColon);
+        ParseExc(nParserExpectedCommaColon,SParserExpectedCommaColon);
       if CurToken=tkComma then
       if CurToken=tkComma then
         ExpectIdentifier;
         ExpectIdentifier;
     Until (CurToken=tkColon);
     Until (CurToken=tkColon);
@@ -2590,19 +2685,31 @@ begin
   Result:=E in FLogEvents;
   Result:=E in FLogEvents;
 end;
 end;
 
 
-procedure TPasParser.DoLog(const Msg: String; SkipSourceInfo: Boolean);
+procedure TPasParser.SetLastMsg(MsgType: TMessageType; MsgNumber: integer;
+  const Fmt: String; Args: array of const);
 begin
 begin
-  If Assigned(FOnLog) then
-    if SkipSourceInfo or not assigned(scanner) then
-      FOnLog(Self,Msg)
-    else
-      FOnLog(Self,Format('%s(%d) : %s',[Scanner.CurFilename,SCanner.CurRow,Msg]));
+  FLastMsgType := MsgType;
+  FLastMsgNumber := MsgNumber;
+  FLastMsgPattern := Fmt;
+  FLastMsg := Format(Fmt,Args);
+  CreateMsgArgs(FLastMsgArgs,Args);
 end;
 end;
 
 
-procedure TPasParser.DoLog(const Fmt: String; Args: array of const;
-  SkipSourceInfo: Boolean);
+procedure TPasParser.DoLog(MsgType: TMessageType; MsgNumber: integer;
+  const Msg: String; SkipSourceInfo: Boolean);
 begin
 begin
-  DoLog(Format(Fmt,Args),SkipSourceInfo);
+  DoLog(MsgType,MsgNumber,Msg,[],SkipSourceInfo);
+end;
+
+procedure TPasParser.DoLog(MsgType: TMessageType; MsgNumber: integer;
+  const Fmt: String; Args: array of const; SkipSourceInfo: Boolean);
+begin
+  SetLastMsg(MsgType,MsgNumber,Fmt,Args);
+  If Assigned(FOnLog) then
+    if SkipSourceInfo or not assigned(scanner) then
+      FOnLog(Self,FLastMsg)
+    else
+      FOnLog(Self,Format('%s(%d) : %s',[Scanner.CurFilename,Scanner.CurRow,FLastMsg]));
 end;
 end;
 
 
 procedure TPasParser.ParseInlineVarDecl(Parent: TPasElement; List: TFPList;
 procedure TPasParser.ParseInlineVarDecl(Parent: TPasElement; List: TFPList;
@@ -2616,7 +2723,7 @@ begin
   if ClosingBrace then
   if ClosingBrace then
    include(tt,tkBraceClose);
    include(tt,tkBraceClose);
   if not (CurToken in tt) then
   if not (CurToken in tt) then
-    ParseExc(SParserExpectedSemiColonEnd);
+    ParseExc(nParserExpectedSemiColonEnd,SParserExpectedSemiColonEnd);
 end;
 end;
 
 
 // Starts after the variable name
 // Starts after the variable name
@@ -2668,7 +2775,7 @@ begin
         end else if CurToken = tkIdentifier then
         end else if CurToken = tkIdentifier then
           Name := CurTokenString
           Name := CurTokenString
         else
         else
-          ParseExc(SParserExpectedConstVarID);
+          ParseExc(nParserExpectedConstVarID,SParserExpectedConstVarID);
         ArgNames.Add(Name);
         ArgNames.Add(Name);
         NextToken;
         NextToken;
         if CurToken = tkColon then
         if CurToken = tkColon then
@@ -2682,7 +2789,7 @@ begin
           break
           break
         end
         end
         else if CurToken <> tkComma then
         else if CurToken <> tkComma then
-          ParseExc(SParserExpectedCommaColon);
+          ParseExc(nParserExpectedCommaColon,SParserExpectedCommaColon);
       end;
       end;
       Value:=Nil;
       Value:=Nil;
       if not IsUntyped then
       if not IsUntyped then
@@ -2695,7 +2802,7 @@ begin
             if (ArgNames.Count>1) then
             if (ArgNames.Count>1) then
               begin
               begin
               FreeAndNil(ArgType);
               FreeAndNil(ArgType);
-              ParseExc(SParserOnlyOneArgumentCanHaveDefault);
+              ParseExc(nParserOnlyOneArgumentCanHaveDefault,SParserOnlyOneArgumentCanHaveDefault);
               end;
               end;
             NextToken;
             NextToken;
             Value := DoParseExpression(Parent,Nil);
             Value := DoParseExpression(Parent,Nil);
@@ -2744,7 +2851,7 @@ begin
   if not Result then
   if not Result then
     begin
     begin
     if Mandatory then
     if Mandatory then
-      ParseExc(SParserExpectedLBracketColon)
+      ParseExc(nParserExpectedLBracketColon,SParserExpectedLBracketColon)
     else
     else
       UngetToken;
       UngetToken;
     end
     end
@@ -2795,7 +2902,7 @@ begin
           begin
           begin
           NextToken;
           NextToken;
           if not (CurToken in [tkString,tkIdentifier]) then
           if not (CurToken in [tkString,tkIdentifier]) then
-            ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkString]]));
+            ParseExcTokenError(TokenInfos[tkString]);
           E:=DoParseExpression(Parent);
           E:=DoParseExpression(Parent);
           if Assigned(P) then
           if Assigned(P) then
             P.LibrarySymbolName:=E;
             P.LibrarySymbolName:=E;
@@ -2820,19 +2927,19 @@ begin
       begin
       begin
       NextToken;  // Should be export name string.
       NextToken;  // Should be export name string.
       if not (CurToken in [tkString,tkIdentifier]) then
       if not (CurToken in [tkString,tkIdentifier]) then
-        ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkString]]));
+        ParseExcTokenError(TokenInfos[tkString]);
       E:=DoParseExpression(Parent);
       E:=DoParseExpression(Parent);
       if parent is TPasProcedure then
       if parent is TPasProcedure then
         TPasProcedure(Parent).PublicName:=E;
         TPasProcedure(Parent).PublicName:=E;
       if (CurToken <> tkSemicolon) then
       if (CurToken <> tkSemicolon) then
-        ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkSemicolon]]));
+        ParseExcTokenError(TokenInfos[tkSemicolon]);
       end;
       end;
     end
     end
   else if (pm=pmForward) then
   else if (pm=pmForward) then
     begin
     begin
     if (Parent.Parent is TInterfaceSection) then
     if (Parent.Parent is TInterfaceSection) then
        begin
        begin
-       ParseExc(SParserForwardNotInterface);
+       ParseExc(nParserForwardNotInterface,SParserForwardNotInterface);
        UngetToken;
        UngetToken;
        end;
        end;
     end
     end
@@ -2912,7 +3019,7 @@ begin
         if (CurToken=tkColon) then
         if (CurToken=tkColon) then
           TPasFunctionType(Element).ResultEl.Name := 'Result'
           TPasFunctionType(Element).ResultEl.Name := 'Result'
         else
         else
-          ParseExc(SParserExpectedColonID);
+          ParseExc(nParserExpectedColonID,SParserExpectedColonID);
         TPasFunctionType(Element).ResultEl.ResultType := ParseType(Parent)
         TPasFunctionType(Element).ResultEl.ResultType := ParseType(Parent)
       end;
       end;
   end;
   end;
@@ -2928,7 +3035,7 @@ begin
       begin
       begin
       expectToken(tkIdentifier);
       expectToken(tkIdentifier);
       if (lowerCase(CurTokenString)<>'nested') then
       if (lowerCase(CurTokenString)<>'nested') then
-        ParseExc(SParserExpectedNested);
+        ParseExc(nParserExpectedNested,SParserExpectedNested);
       Element.isNested:=True;
       Element.isNested:=True;
       end
       end
     else
     else
@@ -3109,13 +3216,13 @@ begin
       else if CurToken = tkIdentifier then
       else if CurToken = tkIdentifier then
         Result.StoredAccessorName := CurTokenString
         Result.StoredAccessorName := CurTokenString
       else
       else
-        ParseExc(SParserSyntaxError);
+        ParseExcSyntaxError;
       NextToken;
       NextToken;
       end;
       end;
     if CurTokenIsIdentifier('DEFAULT') then
     if CurTokenIsIdentifier('DEFAULT') then
       begin
       begin
       if isArray then
       if isArray then
-        ParseExc('Array properties cannot have default value');
+        ParseExc(nParserArrayPropertiesCannotHaveDefaultValue,SParserArrayPropertiesCannotHaveDefaultValue);
       NextToken;
       NextToken;
       Result.DefaultExpr := DoParseExpression(Result);
       Result.DefaultExpr := DoParseExpression(Result);
 //      NextToken;
 //      NextToken;
@@ -3131,7 +3238,7 @@ begin
     if CurTokenIsIdentifier('DEFAULT') then
     if CurTokenIsIdentifier('DEFAULT') then
       begin
       begin
       if (Result.VarType<>Nil) and (not isArray) then
       if (Result.VarType<>Nil) and (not isArray) then
-        ParseExc('The default property must be an array property');
+        ParseExc(nParserDefaultPropertyMustBeArray,SParserDefaultPropertyMustBeArray);
       NextToken;
       NextToken;
       if CurToken = tkSemicolon then
       if CurToken = tkSemicolon then
         begin
         begin
@@ -3318,7 +3425,7 @@ begin
         TPasImplTry(CurBlock).ElseBranch:=TPasImplTryExceptElse(el);
         TPasImplTry(CurBlock).ElseBranch:=TPasImplTryExceptElse(el);
         CurBlock:=TPasImplTryExceptElse(el);
         CurBlock:=TPasImplTryExceptElse(el);
       end else
       end else
-        ParseExc(SParserSyntaxError);
+        ParseExcSyntaxError;
     tkwhile:
     tkwhile:
       begin
       begin
         // while Condition do
         // while Condition do
@@ -3347,7 +3454,7 @@ begin
         Left:=Nil;
         Left:=Nil;
         Right:=Nil;
         Right:=Nil;
         if Not (CurToken in [tkAssign,tkIn]) then
         if Not (CurToken in [tkAssign,tkIn]) then
-          ParseExc(SParserExpectedAssignIn);
+          ParseExc(nParserExpectedAssignIn,SParserExpectedAssignIn);
         if (CurToken=tkAssign) then
         if (CurToken=tkAssign) then
           lt:=ltNormal
           lt:=ltNormal
         else
         else
@@ -3358,14 +3465,14 @@ begin
           if (Lt=ltNormal) then
           if (Lt=ltNormal) then
             begin
             begin
             if Not (CurToken in [tkTo,tkDownTo]) then
             if Not (CurToken in [tkTo,tkDownTo]) then
-              ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkTo]]));
+              ParseExcTokenError(TokenInfos[tkTo]);
             if CurToken=tkdownto then
             if CurToken=tkdownto then
               Lt:=ltDown;
               Lt:=ltDown;
             NextToken;
             NextToken;
             Right:=DoParseExpression(Parent);
             Right:=DoParseExpression(Parent);
             end;
             end;
           if (CurToken<>tkDo) then
           if (CurToken<>tkDo) then
-            ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkDo]]));
+            ParseExcTokenError(TokenInfos[tkDo]);
         except
         except
           FreeAndNil(Left);
           FreeAndNil(Left);
           FreeAndNil(Right);
           FreeAndNil(Right);
@@ -3392,7 +3499,7 @@ begin
         repeat
         repeat
           if CurToken=tkdo then break;
           if CurToken=tkdo then break;
           if CurToken<>tkComma then
           if CurToken<>tkComma then
-            ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkdo]]));
+            ParseExcTokenError(TokenInfos[tkdo]);
           NextToken;
           NextToken;
           Left:=DoParseExpression(Parent);
           Left:=DoParseExpression(Parent);
           //writeln(i,'WITH ...,Expr="',Expr,'" Token=',CurTokenText);
           //writeln(i,'WITH ...,Expr="',Expr,'" Token=',CurTokenText);
@@ -3416,7 +3523,7 @@ begin
           tkend:
           tkend:
             begin
             begin
             if CurBlock.Elements.Count=0 then
             if CurBlock.Elements.Count=0 then
-              ParseExc(SParserExpectCase);
+              ParseExc(nParserExpectCase,SParserExpectCase);
             break; // end without else
             break; // end without else
             end;
             end;
           tkelse:
           tkelse:
@@ -3429,24 +3536,33 @@ begin
             end
             end
           else
           else
             // read case values
             // read case values
-            repeat
-              Left:=DoParseExpression(Parent);
-              //writeln(i,'CASE value="',Expr,'" Token=',CurTokenText);
-              if CurBlock is TPasImplCaseStatement then
-                TPasImplCaseStatement(CurBlock).Expressions.Add(Left)
-              else
-                begin
-                el:=TPasImplCaseStatement(CreateElement(TPasImplCaseStatement,'',CurBlock));
-                TPasImplCaseStatement(el).AddExpression(Left);
-                CurBlock.AddElement(el);
-                CurBlock:=TPasImplCaseStatement(el);
-                end;
-              //writeln(i,'CASE after value Token=',CurTokenText);
-              if (CurToken=tkComma) then
-                NextToken
-              else if (CurToken<>tkColon) then
-                ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkComma]]))
-            until Curtoken=tkColon;
+            if (curToken=tkIdentifier) and (LowerCase(CurtokenString)='otherwise') then
+              begin
+              // create case-else block
+              el:=TPasImplCaseElse(CreateElement(TPasImplCaseElse,'',CurBlock));
+              TPasImplCaseOf(CurBlock).ElseBranch:=TPasImplCaseElse(el);
+              CreateBlock(TPasImplCaseElse(el));
+              break;
+              end
+            else
+              repeat
+                Left:=DoParseExpression(Parent);
+                //writeln(i,'CASE value="',Expr,'" Token=',CurTokenText);
+                if CurBlock is TPasImplCaseStatement then
+                  TPasImplCaseStatement(CurBlock).Expressions.Add(Left)
+                else
+                  begin
+                  el:=TPasImplCaseStatement(CreateElement(TPasImplCaseStatement,'',CurBlock));
+                  TPasImplCaseStatement(el).AddExpression(Left);
+                  CurBlock.AddElement(el);
+                  CurBlock:=TPasImplCaseStatement(el);
+                  end;
+                //writeln(i,'CASE after value Token=',CurTokenText);
+                if (CurToken=tkComma) then
+                  NextToken
+                else if (CurToken<>tkColon) then
+                  ParseExcTokenError(TokenInfos[tkComma]);
+              until Curtoken=tkColon;
             // read statement
             // read statement
             ParseStatement(CurBlock,SubBlock);
             ParseStatement(CurBlock,SubBlock);
             CloseBlock;
             CloseBlock;
@@ -3454,7 +3570,7 @@ begin
             begin
             begin
               NextToken;
               NextToken;
               if not (CurToken in [tkSemicolon,tkelse,tkend]) then
               if not (CurToken in [tkSemicolon,tkelse,tkend]) then
-                ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkSemicolon]]));
+                ParseExcTokenError(TokenInfos[tkSemicolon]);
               if CurToken<>tkSemicolon then
               if CurToken<>tkSemicolon then
                 UngetToken;
                 UngetToken;
             end;
             end;
@@ -3484,7 +3600,7 @@ begin
           TPasImplTry(CurBlock).FinallyExcept:=TPasImplTryFinally(el);
           TPasImplTry(CurBlock).FinallyExcept:=TPasImplTryFinally(el);
           CurBlock:=TPasImplTryFinally(el);
           CurBlock:=TPasImplTryFinally(el);
         end else
         end else
-          ParseExc(SParserSyntaxError);
+          ParseExcSyntaxError;
       end;
       end;
     tkexcept:
     tkexcept:
       begin
       begin
@@ -3500,7 +3616,7 @@ begin
           TPasImplTry(CurBlock).FinallyExcept:=TPasImplTryExcept(el);
           TPasImplTry(CurBlock).FinallyExcept:=TPasImplTryExcept(el);
           CurBlock:=TPasImplTryExcept(el);
           CurBlock:=TPasImplTryExcept(el);
         end else
         end else
-          ParseExc(SParserSyntaxError);
+          ParseExcSyntaxError;
       end;
       end;
     tkon:
     tkon:
       begin
       begin
@@ -3530,7 +3646,7 @@ begin
           CurBlock:=TPasImplExceptOn(el);
           CurBlock:=TPasImplExceptOn(el);
           ExpectToken(tkDo);
           ExpectToken(tkDo);
         end else
         end else
-          ParseExc(SParserSyntaxError);
+          ParseExcSyntaxError;
       end;
       end;
     tkraise:
     tkraise:
       begin
       begin
@@ -3573,7 +3689,7 @@ begin
           if CloseBlock then break; // close try
           if CloseBlock then break; // close try
           if CloseStatement(false) then break;
           if CloseStatement(false) then break;
         end else
         end else
-          ParseExc(SParserSyntaxError);
+          ParseExcSyntaxError;
       end;
       end;
     tkSemiColon:
     tkSemiColon:
       if CloseStatement(true) then break;
       if CloseStatement(true) then break;
@@ -3593,7 +3709,7 @@ begin
           //WriteLn(i,'UNTIL Condition="',Condition,'" Token=',CurTokenString);
           //WriteLn(i,'UNTIL Condition="',Condition,'" Token=',CurTokenString);
           if CloseBlock then break;
           if CloseBlock then break;
         end else
         end else
-          ParseExc(SParserSyntaxError);
+          ParseExcSyntaxError;
       end;
       end;
     else
     else
       left:=DoParseExpression(nil);
       left:=DoParseExpression(nil);
@@ -3619,7 +3735,7 @@ begin
         tkColon:
         tkColon:
         begin
         begin
           if not (left is TPrimitiveExpr) then
           if not (left is TPrimitiveExpr) then
-            ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkSemicolon]]));
+            ParseExcTokenError(TokenInfos[tkSemicolon]);
           // label mark. todo: check mark identifier in the list of labels
           // label mark. todo: check mark identifier in the list of labels
           el:=TPasImplLabelMark(CreateElement(TPasImplLabelMark,'', CurBlock));
           el:=TPasImplLabelMark(CreateElement(TPasImplLabelMark,'', CurBlock));
           TPasImplLabelMark(el).LabelId:=TPrimitiveExpr(left).Value;
           TPasImplLabelMark(el).LabelId:=TPrimitiveExpr(left).Value;
@@ -3651,7 +3767,7 @@ begin
     Labels.Labels.Add(ExpectIdentifier);
     Labels.Labels.Add(ExpectIdentifier);
     NextToken;
     NextToken;
     if not (CurToken in [tkSemicolon, tkComma]) then
     if not (CurToken in [tkSemicolon, tkComma]) then
-      ParseExc(Format(SParserExpectTokenError, [TokenInfos[tkSemicolon]]));
+      ParseExcTokenError(TokenInfos[tkSemicolon]);
   until CurToken=tkSemicolon;
   until CurToken=tkSemicolon;
 end;
 end;
 
 
@@ -3671,7 +3787,7 @@ begin
     ptOperator       : Result:=TPasOperator;
     ptOperator       : Result:=TPasOperator;
     ptClassOperator  : Result:=TPasClassOperator;
     ptClassOperator  : Result:=TPasClassOperator;
   else
   else
-    ParseExc('Unknown procedure Type '+intToStr(Ord(ProcType)));
+    ParseExc(nParserUnknownProcedureType,SParserUnknownProcedureType,[Ord(ProcType)]);
   end;
   end;
 end;
 end;
 
 
@@ -3710,7 +3826,7 @@ begin
     else
     else
       OT:=TPasOperator.NameToOperatorType(CurTokenString);
       OT:=TPasOperator.NameToOperatorType(CurTokenString);
     if (ot=otUnknown) then
     if (ot=otUnknown) then
-      ParseExc(SErrUnknownOperatorType,[CurTokenString]);
+      ParseExc(nErrUnknownOperatorType,SErrUnknownOperatorType,[CurTokenString]);
     Name:=OperatorNames[Ot];
     Name:=OperatorNames[Ot];
     end;
     end;
   PC:=GetProcedureClass(ProcType);
   PC:=GetProcedureClass(ProcType);
@@ -3772,7 +3888,7 @@ begin
       NextToken;
       NextToken;
       V.Values.Add(DoParseExpression(ARec));
       V.Values.Add(DoParseExpression(ARec));
       if Not (CurToken in [tkComma,tkColon]) then
       if Not (CurToken in [tkComma,tkColon]) then
-        ParseExc(SParserExpectedCommaColon);
+        ParseExc(nParserExpectedCommaColon,SParserExpectedCommaColon);
     Until (curToken=tkColon);
     Until (curToken=tkColon);
     ExpectToken(tkBraceOpen);
     ExpectToken(tkBraceOpen);
     NextToken;
     NextToken;
@@ -3826,7 +3942,7 @@ begin
       tkConst:
       tkConst:
         begin
         begin
         if Not AllowMethods then
         if Not AllowMethods then
-          ParseExc(SErrRecordConstantsNotAllowed);
+          ParseExc(nErrRecordConstantsNotAllowed,SErrRecordConstantsNotAllowed);
         ExpectToken(tkIdentifier);
         ExpectToken(tkIdentifier);
         Cons:=ParseConstDecl(ARec);
         Cons:=ParseConstDecl(ARec);
         Cons.Visibility:=v;
         Cons.Visibility:=v;
@@ -3835,15 +3951,15 @@ begin
       tkClass:
       tkClass:
         begin
         begin
         if Not AllowMethods then
         if Not AllowMethods then
-          ParseExc(SErrRecordMethodsNotAllowed);
+          ParseExc(nErrRecordMethodsNotAllowed,SErrRecordMethodsNotAllowed);
         if isClass then
         if isClass then
-          ParseExc(SParserTypeSyntaxError);
+          ParseExc(nParserTypeSyntaxError,SParserTypeSyntaxError);
         isClass:=True;
         isClass:=True;
         end;
         end;
       tkProperty:
       tkProperty:
         begin
         begin
         if Not AllowMethods then
         if Not AllowMethods then
-          ParseExc(SErrRecordPropertiesNotAllowed);
+          ParseExc(nErrRecordPropertiesNotAllowed,SErrRecordPropertiesNotAllowed);
         ExpectToken(tkIdentifier);
         ExpectToken(tkIdentifier);
         Prop:=ParseProperty(ARec,CurtokenString,v);
         Prop:=ParseProperty(ARec,CurtokenString,v);
         Prop.isClass:=isClass;
         Prop.isClass:=isClass;
@@ -3854,7 +3970,7 @@ begin
       tkFunction :
       tkFunction :
         begin
         begin
         if Not AllowMethods then
         if Not AllowMethods then
-          ParseExc(SErrRecordMethodsNotAllowed);
+          ParseExc(nErrRecordMethodsNotAllowed,SErrRecordMethodsNotAllowed);
         ProcType:=GetProcTypeFromtoken(CurToken,isClass);
         ProcType:=GetProcTypeFromtoken(CurToken,isClass);
         Proc:=ParseProcedureOrFunctionDecl(ARec,ProcType,v);
         Proc:=ParseProcedureOrFunctionDecl(ARec,ProcType,v);
         if Proc.Parent is TPasOverloadedProc then
         if Proc.Parent is TPasOverloadedProc then
@@ -3868,9 +3984,9 @@ begin
           if CheckVisibility(CurtokenString,v) then
           if CheckVisibility(CurtokenString,v) then
             begin
             begin
             If not (po_delphi in Scanner.Options) then
             If not (po_delphi in Scanner.Options) then
-              ParseExc(SErrRecordVisibilityNotAllowed);
+              ParseExc(nErrRecordVisibilityNotAllowed,SErrRecordVisibilityNotAllowed);
             if not (v in [visPrivate,visPublic,visStrictPrivate]) then
             if not (v in [visPrivate,visPublic,visStrictPrivate]) then
-              ParseExc(SParserInvalidRecordVisibility);
+              ParseExc(nParserInvalidRecordVisibility,SParserInvalidRecordVisibility);
             NextToken;
             NextToken;
             Continue;
             Continue;
             end;
             end;
@@ -3894,7 +4010,7 @@ begin
         ParseRecordVariantParts(ARec,AEndToken);
         ParseRecordVariantParts(ARec,AEndToken);
         end;
         end;
     else
     else
-      ParseExc(SParserTypeSyntaxError);
+      ParseExc(nParserTypeSyntaxError,SParserTypeSyntaxError);
     end;
     end;
     If CurToken<>tkClass then
     If CurToken<>tkClass then
       isClass:=False;
       isClass:=False;
@@ -3963,11 +4079,11 @@ begin
         visPrivate   : AVisibility:=visStrictPrivate;
         visPrivate   : AVisibility:=visStrictPrivate;
         visProtected : AVisibility:=visStrictProtected;
         visProtected : AVisibility:=visStrictProtected;
       else
       else
-        ParseExc(Format(SParserStrangeVisibility,[S]));
+        ParseExc(nParserStrangeVisibility,SParserStrangeVisibility,[S]);
       end
       end
     end
     end
   else if B then
   else if B then
-    ParseExc(SParserExpectVisibility);
+    ParseExc(nParserExpectVisibility,SParserExpectVisibility);
 end;
 end;
 
 
 procedure TPasParser.ProcessMethod(AType: TPasClassType; IsClass : Boolean; AVisibility : TPasMemberVisibility);
 procedure TPasParser.ProcessMethod(AType: TPasClassType; IsClass : Boolean; AVisibility : TPasMemberVisibility);
@@ -4073,7 +4189,7 @@ begin
       tkIdentifier:
       tkIdentifier:
         begin
         begin
         if (AType.ObjKind=okInterface) then
         if (AType.ObjKind=okInterface) then
-          ParseExc(SParserNoFieldsAllowed);
+          ParseExc(nParserNoFieldsAllowed,SParserNoFieldsAllowed);
         if CurToken=tkVar then
         if CurToken=tkVar then
           ExpectToken(tkIdentifier);
           ExpectToken(tkIdentifier);
         SaveComments;
         SaveComments;
@@ -4084,7 +4200,7 @@ begin
         begin
         begin
         SaveComments;
         SaveComments;
         if (Curtoken in [tkConstructor,tkDestructor]) and (AType.ObjKind in [okInterface,okRecordHelper]) then
         if (Curtoken in [tkConstructor,tkDestructor]) and (AType.ObjKind in [okInterface,okRecordHelper]) then
-          ParseExc(SParserNoConstructorAllowed);
+          ParseExc(nParserNoConstructorAllowed,SParserNoConstructorAllowed);
         ProcessMethod(AType,False,CurVisibility);
         ProcessMethod(AType,False,CurVisibility);
         end;
         end;
       tkclass:
       tkclass:
@@ -4104,7 +4220,7 @@ begin
            AType.Members.Add(ParseProperty(AType,CurtokenString,CurVisibility));
            AType.Members.Add(ParseProperty(AType,CurtokenString,CurVisibility));
            end
            end
          else
          else
-           ParseExc(SParserTypeSyntaxError)
+           ParseExc(nParserTypeSyntaxError,SParserTypeSyntaxError)
         end;
         end;
       tkProperty:
       tkProperty:
         begin
         begin
@@ -4155,7 +4271,7 @@ begin
   if (AType.ObjKind in [okClassHelper,okRecordHelper]) then
   if (AType.ObjKind in [okClassHelper,okRecordHelper]) then
     begin
     begin
     if (CurToken<>tkFor) then
     if (CurToken<>tkFor) then
-      ParseExc(Format(SParserExpectTokenError,[TokenInfos[tkFor]]));
+      ParseExcTokenError(TokenInfos[tkFor]);
     AType.HelperForType:=ParseType(Nil);
     AType.HelperForType:=ParseType(Nil);
     NextToken;
     NextToken;
     end;
     end;
@@ -4168,7 +4284,7 @@ begin
       NextToken;
       NextToken;
       AType.GUIDExpr:=DoParseExpression(AType);
       AType.GUIDExpr:=DoParseExpression(AType);
       if (CurToken<>tkSquaredBraceClose) then
       if (CurToken<>tkSquaredBraceClose) then
-        ParseExc(Format(SParserExpectTokenError,[TokenInfos[tkSquaredBraceClose]]));
+        ParseExcTokenError(TokenInfos[tkSquaredBraceClose]);
       NextToken;
       NextToken;
       end;
       end;
     ParseClassMembers(AType);
     ParseClassMembers(AType);
@@ -4202,7 +4318,7 @@ begin
   if (CurToken = tkHelper) then
   if (CurToken = tkHelper) then
     begin
     begin
     if Not (AObjKind in [okClass,okRecordHelper]) then
     if Not (AObjKind in [okClass,okRecordHelper]) then
-      ParseExc(Format(SParserHelperNotAllowed,[ObjKindNames[AObjKind]]));
+      ParseExc(nParserHelperNotAllowed,SParserHelperNotAllowed,[ObjKindNames[AObjKind]]);
     if (AObjKind = okClass)  then
     if (AObjKind = okClass)  then
       AObjKind:=okClassHelper;
       AObjKind:=okClassHelper;
     NextToken;
     NextToken;

+ 132 - 39
packages/fcl-passrc/src/pscanner.pp

@@ -23,6 +23,24 @@ interface
 
 
 uses SysUtils, Classes;
 uses SysUtils, Classes;
 
 
+// message numbers
+const
+  nErrInvalidCharacter = 1001;
+  nErrOpenString = 1002;
+  nErrIncludeFileNotFound = 1003;
+  nErrIfXXXNestingLimitReached = 1004;
+  nErrInvalidPPElse = 1005;
+  nErrInvalidPPEndif = 1006;
+  nLogOpeningFile = 1007;
+  nLogLineNumber = 1008;
+  nLogIFDefAccepted = 1009;
+  nLogIFDefRejected = 1010;
+  nLogIFNDefAccepted = 1011;
+  nLogIFNDefRejected = 1012;
+  nLogIFOPTIgnored = 1013;
+  nLogIFIgnored = 1014;
+
+// resourcestring patterns of messages
 resourcestring
 resourcestring
   SErrInvalidCharacter = 'Invalid character ''%s''';
   SErrInvalidCharacter = 'Invalid character ''%s''';
   SErrOpenString = 'string exceeds end of line';
   SErrOpenString = 'string exceeds end of line';
@@ -40,6 +58,18 @@ resourcestring
   SLogIFIgnored = 'IF %s found, ignoring (rejected).';
   SLogIFIgnored = 'IF %s found, ignoring (rejected).';
 
 
 type
 type
+  TMessageType = (
+    mtFatal,
+    mtError,
+    mtWarning,
+    mtNote,
+    mtHint,
+    mtInfo,
+    mtDebug
+    );
+  TMessageTypes = set of TMessageType;
+
+  TMessageArgs = array of string;
 
 
   TToken = (
   TToken = (
     tkEOF,
     tkEOF,
@@ -305,6 +335,11 @@ type
 
 
   TPascalScanner = class
   TPascalScanner = class
   private
   private
+    FLastMsg: string;
+    FLastMsgArgs: TMessageArgs;
+    FLastMsgNumber: integer;
+    FLastMsgPattern: string;
+    FLastMsgType: TMessageType;
     FFileResolver: TBaseFileResolver;
     FFileResolver: TBaseFileResolver;
     FCurSourceFile: TLineReader;
     FCurSourceFile: TLineReader;
     FCurFilename: string;
     FCurFilename: string;
@@ -332,10 +367,11 @@ type
     function GetCurColumn: Integer;
     function GetCurColumn: Integer;
     procedure SetOptions(AValue: TPOptions);
     procedure SetOptions(AValue: TPOptions);
   protected
   protected
-    Procedure DoLog(Const Msg : String; SkipSourceInfo : Boolean = False);overload;
-    Procedure DoLog(Const Fmt : String; Args : Array of const;SkipSourceInfo : Boolean = False);overload;
-    procedure Error(const Msg: string);overload;
-    procedure Error(const Msg: string; Args: array of Const);overload;
+    procedure SetCurMsg(MsgType: TMessageType; MsgNumber: integer; Const Fmt : String; Args : Array of const);
+    Procedure DoLog(MsgType: TMessageType; MsgNumber: integer; Const Msg : String; SkipSourceInfo : Boolean = False);overload;
+    Procedure DoLog(MsgType: TMessageType; MsgNumber: integer; Const Fmt : String; Args : Array of const;SkipSourceInfo : Boolean = False);overload;
+    procedure Error(MsgNumber: integer; const Msg: string);overload;
+    procedure Error(MsgNumber: integer; const Fmt: string; Args: array of Const);overload;
     procedure HandleDefine(Param: String); virtual;
     procedure HandleDefine(Param: String); virtual;
     procedure HandleIncludeFile(Param: String); virtual;
     procedure HandleIncludeFile(Param: String); virtual;
     procedure HandleUnDefine(Param: String);virtual;
     procedure HandleUnDefine(Param: String);virtual;
@@ -372,6 +408,12 @@ type
     Property Options : TPOptions Read FOptions Write SetOptions;
     Property Options : TPOptions Read FOptions Write SetOptions;
     Property LogEvents : TPScannerLogEvents Read FLogEvents Write FLogEvents;
     Property LogEvents : TPScannerLogEvents Read FLogEvents Write FLogEvents;
     Property OnLog : TPScannerLogHandler Read FOnLog Write FOnLog;
     Property OnLog : TPScannerLogHandler Read FOnLog Write FOnLog;
+
+    property LastMsg: string read FLastMsg write FLastMsg;
+    property LastMsgNumber: integer read FLastMsgNumber write FLastMsgNumber;
+    property LastMsgType: TMessageType read FLastMsgType write FLastMsgType;
+    property LastMsgPattern: string read FLastMsgPattern write FLastMsgPattern;
+    property LastMsgArgs: TMessageArgs read FLastMsgArgs write FLastMsgArgs;
   end;
   end;
 
 
 const
 const
@@ -496,6 +538,8 @@ function FilenameIsWinAbsolute(const TheFilename: string): boolean;
 function FilenameIsUnixAbsolute(const TheFilename: string): boolean;
 function FilenameIsUnixAbsolute(const TheFilename: string): boolean;
 function IsNamedToken(Const AToken : String; Out T : TToken) : Boolean;
 function IsNamedToken(Const AToken : String; Out T : TToken) : Boolean;
 
 
+procedure CreateMsgArgs(var MsgArgs: TMessageArgs; Args: array of const);
+
 implementation
 implementation
 
 
 Var
 Var
@@ -576,6 +620,39 @@ begin
     T:=SortedTokens[I];
     T:=SortedTokens[I];
 end;
 end;
 
 
+procedure CreateMsgArgs(var MsgArgs: TMessageArgs; Args: array of const);
+var
+  i: Integer;
+begin
+  SetLength(MsgArgs, High(Args)-Low(Args)+1);
+  for i:=Low(Args) to High(Args) do
+  begin
+    case Args[i].VType of
+      vtInteger:      MsgArgs[i] := IntToStr(Args[i].VInteger);
+      vtBoolean:      MsgArgs[i] := BoolToStr(Args[i].VBoolean);
+      vtChar:         MsgArgs[i] := Args[i].VChar;
+      {$ifndef FPUNONE}
+      vtExtended:     ; //  Args[i].VExtended^;
+      {$ENDIF}
+      vtString:       MsgArgs[i] := Args[i].VString^;
+      vtPointer:      ; //  Args[i].VPointer;
+      vtPChar:        MsgArgs[i] := Args[i].VPChar;
+      vtObject:       ; //  Args[i].VObject;
+      vtClass:        ; //  Args[i].VClass;
+      vtWideChar:     MsgArgs[i] := AnsiString(Args[i].VWideChar);
+      vtPWideChar:    MsgArgs[i] := Args[i].VPWideChar;
+      vtAnsiString:   MsgArgs[i] := AnsiString(Args[i].VAnsiString);
+      vtCurrency:     ; //  Args[i].VCurrency^);
+      vtVariant:      ; //  Args[i].VVariant^);
+      vtInterface:    ; //  Args[i].VInterface^);
+      vtWidestring:   MsgArgs[i] := AnsiString(WideString(Args[i].VWideString));
+      vtInt64:        MsgArgs[i] := IntToStr(Args[i].VInt64^);
+      vtQWord:        MsgArgs[i] := IntToStr(Args[i].VQWord^);
+      vtUnicodeString:MsgArgs[i] := AnsiString(UnicodeString(Args[i].VUnicodeString));
+    end;
+  end;
+end;
+
 type
 type
   TIncludeStackItem = class
   TIncludeStackItem = class
     SourceFile: TLineReader;
     SourceFile: TLineReader;
@@ -1020,7 +1097,7 @@ begin
   Clearfiles;
   Clearfiles;
   FCurSourceFile := FileResolver.FindSourceFile(AFilename);
   FCurSourceFile := FileResolver.FindSourceFile(AFilename);
   if LogEvent(sleFile) then
   if LogEvent(sleFile) then
-    DoLog(SLogOpeningFile,[AFileName],True);
+    DoLog(mtInfo,nLogOpeningFile,SLogOpeningFile,[AFileName],True);
   FCurFilename := AFilename;
   FCurFilename := AFilename;
   FileResolver.BaseDirectory := IncludeTrailingPathDelimiter(ExtractFilePath(AFilename));
   FileResolver.BaseDirectory := IncludeTrailingPathDelimiter(ExtractFilePath(AFilename));
 end;
 end;
@@ -1069,14 +1146,17 @@ begin
 //  Writeln(Result, '(',CurTokenString,')');
 //  Writeln(Result, '(',CurTokenString,')');
 end;
 end;
 
 
-procedure TPascalScanner.Error(const Msg: string);
+procedure TPascalScanner.Error(MsgNumber: integer; const Msg: string);
 begin
 begin
+  SetCurMsg(mtError,MsgNumber,Msg,[]);
   raise EScannerError.Create(Msg);
   raise EScannerError.Create(Msg);
 end;
 end;
 
 
-procedure TPascalScanner.Error(const Msg: string; Args: array of Const);
+procedure TPascalScanner.Error(MsgNumber: integer; const Fmt: string;
+  Args: array of const);
 begin
 begin
-  raise EScannerError.CreateFmt(Msg, Args);
+  SetCurMsg(mtError,MsgNumber,Fmt,Args);
+  raise EScannerError.CreateFmt(Fmt, Args);
 end;
 end;
 
 
 function TPascalScanner.DoFetchTextToken:TToken;
 function TPascalScanner.DoFetchTextToken:TToken;
@@ -1122,7 +1202,7 @@ begin
                 break;
                 break;
 
 
             if TokenStr[0] = #0 then
             if TokenStr[0] = #0 then
-              Error(SErrOpenString);
+              Error(nErrOpenString,SErrOpenString);
 
 
             Inc(TokenStr);
             Inc(TokenStr);
           end;
           end;
@@ -1141,7 +1221,7 @@ begin
 
 
 end;
 end;
 
 
-Procedure TPascalScanner.PushStackItem;
+procedure TPascalScanner.PushStackItem;
 
 
 Var
 Var
   SI: TIncludeStackItem;
   SI: TIncludeStackItem;
@@ -1160,7 +1240,7 @@ begin
   FCurRow := 0;
   FCurRow := 0;
 end;
 end;
 
 
-Procedure TPascalScanner.HandleIncludeFile(Param : String);
+procedure TPascalScanner.HandleIncludeFile(Param: String);
 
 
 begin
 begin
   PushStackItem;
   PushStackItem;
@@ -1171,12 +1251,12 @@ begin
     end;
     end;
   FCurSourceFile := FileResolver.FindIncludeFile(Param);
   FCurSourceFile := FileResolver.FindIncludeFile(Param);
   if not Assigned(FCurSourceFile) then
   if not Assigned(FCurSourceFile) then
-    Error(SErrIncludeFileNotFound, [Param]);
+    Error(nErrIncludeFileNotFound, SErrIncludeFileNotFound, [Param]);
   FCurFilename := Param;
   FCurFilename := Param;
   if FCurSourceFile is TFileLineReader then
   if FCurSourceFile is TFileLineReader then
     FCurFilename := TFileLineReader(FCurSourceFile).Filename; // nicer error messages
     FCurFilename := TFileLineReader(FCurSourceFile).Filename; // nicer error messages
   If LogEvent(sleFile) then
   If LogEvent(sleFile) then
-    DoLog(SLogOpeningFile,[FCurFileName],True);
+    DoLog(mtInfo,nLogOpeningFile,SLogOpeningFile,[FCurFileName],True);
 end;
 end;
 
 
 function TPascalScanner.HandleMacro(AIndex : integer) : TToken;
 function TPascalScanner.HandleMacro(AIndex : integer) : TToken;
@@ -1196,7 +1276,7 @@ begin
 //  Writeln(Result,Curtoken);
 //  Writeln(Result,Curtoken);
 end;
 end;
 
 
-Procedure TPascalScanner.HandleDefine(Param : String);
+procedure TPascalScanner.HandleDefine(Param: String);
 
 
 Var
 Var
   Index : Integer;
   Index : Integer;
@@ -1220,7 +1300,7 @@ begin
     end;
     end;
 end;
 end;
 
 
-Procedure TPascalScanner.HandleUnDefine(Param : String);
+procedure TPascalScanner.HandleUnDefine(Param: String);
 
 
 Var
 Var
   Index : integer;
   Index : integer;
@@ -1257,7 +1337,7 @@ function TPascalScanner.DoFetchToken: TToken;
       Result := true;
       Result := true;
       Inc(FCurRow);
       Inc(FCurRow);
       if LogEvent(sleLineNumber) and ((FCurRow Mod 100) = 0) then
       if LogEvent(sleLineNumber) and ((FCurRow Mod 100) = 0) then
-        DoLog(SLogLineNumber,[FCurRow],True);
+        DoLog(mtInfo,nLogLineNumber,SLogLineNumber,[FCurRow],True);
     end;
     end;
   end;
   end;
 
 
@@ -1660,7 +1740,7 @@ begin
             if (Directive = 'IFDEF') then
             if (Directive = 'IFDEF') then
               begin
               begin
               if PPSkipStackIndex = High(PPSkipModeStack) then
               if PPSkipStackIndex = High(PPSkipModeStack) then
-                Error(SErrIfXXXNestingLimitReached);
+                Error(nErrIfXXXNestingLimitReached,SErrIfXXXNestingLimitReached);
               PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
               PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
               PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
               PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
               Inc(PPSkipStackIndex);
               Inc(PPSkipStackIndex);
@@ -1682,14 +1762,14 @@ begin
                   PPSkipMode := ppSkipElseBranch;
                   PPSkipMode := ppSkipElseBranch;
                 If LogEvent(sleConditionals) then
                 If LogEvent(sleConditionals) then
                   if PPSkipMode=ppSkipElseBranch then
                   if PPSkipMode=ppSkipElseBranch then
-                    DoLog(SLogIFDefAccepted,[Param])
+                    DoLog(mtInfo,nLogIFDefAccepted,sLogIFDefAccepted,[Param])
                   else
                   else
-                    DoLog(SLogIFDefRejected,[Param])
+                    DoLog(mtInfo,nLogIFDefRejected,sLogIFDefRejected,[Param])
               end;
               end;
             end else if Directive = 'IFNDEF' then
             end else if Directive = 'IFNDEF' then
             begin
             begin
               if PPSkipStackIndex = High(PPSkipModeStack) then
               if PPSkipStackIndex = High(PPSkipModeStack) then
-                Error(SErrIfXXXNestingLimitReached);
+                Error(nErrIfXXXNestingLimitReached,sErrIfXXXNestingLimitReached);
               PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
               PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
               PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
               PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
               Inc(PPSkipStackIndex);
               Inc(PPSkipStackIndex);
@@ -1709,14 +1789,14 @@ begin
                   PPSkipMode := ppSkipElseBranch;
                   PPSkipMode := ppSkipElseBranch;
                 If LogEvent(sleConditionals) then
                 If LogEvent(sleConditionals) then
                   if PPSkipMode=ppSkipElseBranch then
                   if PPSkipMode=ppSkipElseBranch then
-                    DoLog(SLogIFNDefAccepted,[Param])
+                    DoLog(mtInfo,nLogIFNDefAccepted,sLogIFNDefAccepted,[Param])
                   else
                   else
-                    DoLog(SLogIFNDefRejected,[Param])
+                    DoLog(mtInfo,nLogIFNDefRejected,sLogIFNDefRejected,[Param])
               end;
               end;
             end else if Directive = 'IFOPT' then
             end else if Directive = 'IFOPT' then
             begin
             begin
               if PPSkipStackIndex = High(PPSkipModeStack) then
               if PPSkipStackIndex = High(PPSkipModeStack) then
-                Error(SErrIfXXXNestingLimitReached);
+                Error(nErrIfXXXNestingLimitReached,sErrIfXXXNestingLimitReached);
               PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
               PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
               PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
               PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
               Inc(PPSkipStackIndex);
               Inc(PPSkipStackIndex);
@@ -1732,11 +1812,11 @@ begin
                 PPIsSkipping := true;
                 PPIsSkipping := true;
               end;
               end;
               If LogEvent(sleConditionals) then
               If LogEvent(sleConditionals) then
-                DoLog(SLogIFOPTIgnored,[Uppercase(Param)])
+                DoLog(mtInfo,nLogIFOPTIgnored,sLogIFOPTIgnored,[Uppercase(Param)])
             end else if Directive = 'IF' then
             end else if Directive = 'IF' then
             begin
             begin
               if PPSkipStackIndex = High(PPSkipModeStack) then
               if PPSkipStackIndex = High(PPSkipModeStack) then
-                Error(SErrIfXXXNestingLimitReached);
+                Error(nErrIfXXXNestingLimitReached,sErrIfXXXNestingLimitReached);
               PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
               PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
               PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
               PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
               Inc(PPSkipStackIndex);
               Inc(PPSkipStackIndex);
@@ -1751,12 +1831,12 @@ begin
                 PPSkipMode := ppSkipIfBranch;
                 PPSkipMode := ppSkipIfBranch;
                 PPIsSkipping := true;
                 PPIsSkipping := true;
               If LogEvent(sleConditionals) then
               If LogEvent(sleConditionals) then
-                 DoLog(SLogIFIgnored,[Uppercase(Param)])
+                 DoLog(mtInfo,nLogIFIgnored,sLogIFIgnored,[Uppercase(Param)])
               end;
               end;
             end else if Directive = 'ELSE' then
             end else if Directive = 'ELSE' then
             begin
             begin
               if PPSkipStackIndex = 0 then
               if PPSkipStackIndex = 0 then
-                Error(SErrInvalidPPElse);
+                Error(nErrInvalidPPElse,sErrInvalidPPElse);
               if PPSkipMode = ppSkipIfBranch then
               if PPSkipMode = ppSkipIfBranch then
                 PPIsSkipping := false
                 PPIsSkipping := false
               else if PPSkipMode = ppSkipElseBranch then
               else if PPSkipMode = ppSkipElseBranch then
@@ -1764,7 +1844,7 @@ begin
             end else if ((Directive = 'ENDIF') or (Directive='IFEND')) then
             end else if ((Directive = 'ENDIF') or (Directive='IFEND')) then
             begin
             begin
               if PPSkipStackIndex = 0 then
               if PPSkipStackIndex = 0 then
-                Error(SErrInvalidPPEndif);
+                Error(nErrInvalidPPEndif,sErrInvalidPPEndif);
               Dec(PPSkipStackIndex);
               Dec(PPSkipStackIndex);
               PPSkipMode := PPSkipModeStack[PPSkipStackIndex];
               PPSkipMode := PPSkipModeStack[PPSkipStackIndex];
               PPIsSkipping := PPIsSkippingStack[PPSkipStackIndex];
               PPIsSkipping := PPIsSkippingStack[PPSkipStackIndex];
@@ -1800,7 +1880,7 @@ begin
     if PPIsSkipping then
     if PPIsSkipping then
       Inc(TokenStr)
       Inc(TokenStr)
     else
     else
-      Error(SErrInvalidCharacter, [TokenStr[0]]);
+      Error(nErrInvalidCharacter, SErrInvalidCharacter, [TokenStr[0]]);
   end;
   end;
 
 
   FCurToken := Result;
   FCurToken := Result;
@@ -1819,18 +1899,21 @@ begin
     Result:=0;
     Result:=0;
 end;
 end;
 
 
-procedure TPascalScanner.DoLog(const Msg: String;SkipSourceInfo : Boolean = False);
+procedure TPascalScanner.DoLog(MsgType: TMessageType; MsgNumber: integer;
+  const Msg: String; SkipSourceInfo: Boolean);
 begin
 begin
-  If Assigned(FOnLog) then
-    if SkipSourceInfo then
-      FOnLog(Self,Msg)
-    else
-      FOnLog(Self,Format('%s(%d) : %s',[FCurFileName,FCurRow,Msg]));
+  DoLog(MsgType,MsgNumber,Msg,[],SkipSourceInfo);
 end;
 end;
 
 
-procedure TPascalScanner.DoLog(const Fmt: String; Args: array of const;SkipSourceInfo : Boolean = False);
+procedure TPascalScanner.DoLog(MsgType: TMessageType; MsgNumber: integer;
+  const Fmt: String; Args: array of const; SkipSourceInfo: Boolean);
 begin
 begin
-  DoLog(Format(Fmt,Args),SkipSourceInfo);
+  SetCurMsg(MsgType,MsgNumber,Fmt,Args);
+  If Assigned(FOnLog) then
+    if SkipSourceInfo then
+      FOnLog(Self,FLastMsg)
+    else
+      FOnLog(Self,Format('%s(%d) : %s',[FCurFileName,FCurRow,FLastMsg]));
 end;
 end;
 
 
 procedure TPascalScanner.SetOptions(AValue: TPOptions);
 procedure TPascalScanner.SetOptions(AValue: TPOptions);
@@ -1839,14 +1922,24 @@ begin
   FOptions:=AValue;
   FOptions:=AValue;
 end;
 end;
 
 
-Procedure TPascalScanner.AddDefine(S : String);
+procedure TPascalScanner.SetCurMsg(MsgType: TMessageType; MsgNumber: integer;
+  const Fmt: String; Args: array of const);
+begin
+  FLastMsgType := MsgType;
+  FLastMsgNumber := MsgNumber;
+  FLastMsgPattern := Fmt;
+  FLastMsg := Format(Fmt,Args);
+  CreateMsgArgs(FLastMsgArgs,Args);
+end;
+
+procedure TPascalScanner.AddDefine(S: String);
 
 
 begin
 begin
   If FDefines.IndexOf(S)=-1 then
   If FDefines.IndexOf(S)=-1 then
     FDefines.Add(S);
     FDefines.Add(S);
 end;
 end;
 
 
-Procedure TPascalScanner.RemoveDefine(S : String);
+procedure TPascalScanner.RemoveDefine(S: String);
 
 
 Var
 Var
   I : Integer;
   I : Integer;

+ 18 - 0
packages/fcl-passrc/tests/tcstatements.pas

@@ -75,6 +75,7 @@ Type
     Procedure TestCase2Cases;
     Procedure TestCase2Cases;
     Procedure TestCaseBlock;
     Procedure TestCaseBlock;
     Procedure TestCaseElseBlockEmpty;
     Procedure TestCaseElseBlockEmpty;
+    procedure TestCaseOtherwiseBlockEmpty;
     Procedure TestCaseElseBlockAssignment;
     Procedure TestCaseElseBlockAssignment;
     Procedure TestCaseElseBlock2Assignments;
     Procedure TestCaseElseBlock2Assignments;
     Procedure TestCaseIfCaseElse;
     Procedure TestCaseIfCaseElse;
@@ -974,6 +975,23 @@ begin
   AssertEquals('Zero statements ',0,TPasImplCaseElse(C.ElseBranch).Elements.Count);
   AssertEquals('Zero statements ',0,TPasImplCaseElse(C.ElseBranch).Elements.Count);
 end;
 end;
 
 
+procedure TTestStatementParser.TestCaseOtherwiseBlockEmpty;
+
+Var
+  C : TPasImplCaseOf;
+  S : TPasImplCaseStatement;
+  B : TPasImplbeginBlock;
+
+begin
+  DeclareVar('integer');
+  TestStatement(['case a of','1 : begin end;','otherwise',' end;']);
+  C:=AssertStatement('Case statement',TpasImplCaseOf) as TpasImplCaseOf;
+  AssertNotNull('Have case expression',C.CaseExpr);
+  AssertNotNull('Have else branch',C.ElseBranch);
+  AssertEquals('Correct else branch class',TPasImplCaseElse,C.ElseBranch.ClassType);
+  AssertEquals('Zero statements ',0,TPasImplCaseElse(C.ElseBranch).Elements.Count);
+end;
+
 procedure TTestStatementParser.TestCaseElseBlockAssignment;
 procedure TTestStatementParser.TestCaseElseBlockAssignment;
 Var
 Var
   C : TPasImplCaseOf;
   C : TPasImplCaseOf;

+ 57 - 34
utils/fpdoc/css.inc

@@ -1,6 +1,6 @@
 
 
 Const
 Const
-  DefaultCSS : Array[0..2254] of byte = (
+  DefaultCSS : Array[0..2649] of byte = (
       47, 42, 10, 32, 32, 36, 73,100, 58, 32,102,112,100,111, 99, 46, 99,
       47, 42, 10, 32, 32, 36, 73,100, 58, 32,102,112,100,111, 99, 46, 99,
      115,115, 44,118, 32, 49, 46, 49, 32, 50, 48, 48, 51, 47, 48, 51, 47,
      115,115, 44,118, 32, 49, 46, 49, 32, 50, 48, 48, 51, 47, 48, 51, 47,
       49, 55, 32, 50, 51, 58, 48, 51, 58, 50, 48, 32,109,105, 99,104, 97,
       49, 55, 32, 50, 51, 58, 48, 51, 58, 50, 48, 32,109,105, 99,104, 97,
@@ -100,37 +100,60 @@ Const
      111,108,111,114, 58, 32, 35,102,102,102,102, 99, 48, 59, 10,125, 10,
      111,108,111,114, 58, 32, 35,102,102,102,102, 99, 48, 59, 10,125, 10,
       10,116, 97, 98,108,101, 46, 98, 97,114, 32,123, 10, 32, 32, 98, 97,
       10,116, 97, 98,108,101, 46, 98, 97,114, 32,123, 10, 32, 32, 98, 97,
       99,107,103,114,111,117,110,100, 45, 99,111,108,111,114, 58, 32, 35,
       99,107,103,114,111,117,110,100, 45, 99,111,108,111,114, 58, 32, 35,
-      97, 48, 99, 48,102,102, 59, 10,125, 10, 10,115,112, 97,110, 46, 98,
-      97,114,116,105,116,108,101, 32,123, 10, 32, 32,102,111,110,116, 45,
-     119,101,105,103,104,116, 58, 32, 98,111,108,100, 59, 10, 32, 32,102,
-     111,110,116, 45,115,116,121,108,101, 58, 32,105,116, 97,108,105, 99,
-      59, 10, 32, 32, 99,111,108,111,114, 58, 32,100, 97,114,107, 98,108,
-     117,101, 10,125, 10, 10,115,112, 97,110, 46,102,111,111,116,101,114,
-      32,123, 10, 32, 32,102,111,110,116, 45,115,116,121,108,101, 58, 32,
-     105,116, 97,108,105, 99, 59, 10, 32, 32, 99,111,108,111,114, 58, 32,
-     100, 97,114,107, 98,108,117,101, 10,125, 10, 10, 47, 42, 32,100,101,
-     102,105,110,105,116,105,111,110, 32,108,105,115,116, 32, 42, 47, 10,
-     100,108, 32,123, 10, 32, 98,111,114,100,101,114, 58, 32, 51,112,120,
-      32,100,111,117, 98,108,101, 32, 35, 99, 99, 99, 59, 10, 32,112, 97,
-     100,100,105,110,103, 58, 32, 48, 46, 53,101,109, 59, 10,125, 10, 10,
+      97, 48, 99, 48,102,102, 59, 10,125, 10, 10,116,100, 32,112, 32,123,
+      10, 32,109, 97,114,103,105,110, 58, 32, 48, 59, 10,125, 10, 10,115,
+     112, 97,110, 46, 98, 97,114,116,105,116,108,101, 32,123, 10, 32, 32,
+     102,111,110,116, 45,119,101,105,103,104,116, 58, 32, 98,111,108,100,
+      59, 10, 32, 32,102,111,110,116, 45,115,116,121,108,101, 58, 32,105,
+     116, 97,108,105, 99, 59, 10, 32, 32, 99,111,108,111,114, 58, 32,100,
+      97,114,107, 98,108,117,101, 10,125, 10, 10,115,112, 97,110, 46,102,
+     111,111,116,101,114, 32,123, 10, 32, 32,102,111,110,116, 45,115,116,
+     121,108,101, 58, 32,105,116, 97,108,105, 99, 59, 10, 32, 32, 99,111,
+     108,111,114, 58, 32,100, 97,114,107, 98,108,117,101, 10,125, 10, 10,
       47, 42, 32,100,101,102,105,110,105,116,105,111,110, 32,108,105,115,
       47, 42, 32,100,101,102,105,110,105,116,105,111,110, 32,108,105,115,
-     116, 58, 32,116,101,114,109, 32, 42, 47, 10,100,116, 32,123, 10, 32,
-     102,108,111, 97,116, 58, 32,108,101,102,116, 59, 10, 32, 99,108,101,
-      97,114, 58, 32,108,101,102,116, 59, 10, 32,119,105,100,116,104, 58,
-      32, 97,117,116,111, 59, 32, 47, 42, 32,110,111,114,109, 97,108,108,
-     121, 32, 98,114,111,119,115,101,114,115, 32,100,101,102, 97,117,108,
-     116, 32,119,105,100,116,104, 32,111,102, 32,108, 97,114,103,101,115,
-     116, 32,105,116,101,109, 32, 42, 47, 10, 32,112, 97,100,100,105,110,
-     103, 45,114,105,103,104,116, 58, 32, 50, 48,112,120, 59, 10, 32,102,
-     111,110,116, 45,119,101,105,103,104,116, 58, 32, 98,111,108,100, 59,
-      10, 32, 99,111,108,111,114, 58, 32,100, 97,114,107,103,114,101,101,
-     110, 59, 10,125, 10, 10, 47, 42, 32,100,101,102,105,110,105,116,105,
-     111,110, 32,108,105,115,116, 58, 32,100,101,115, 99,114,105,112,116,
-     105,111,110, 32, 42, 47, 10,100,100, 32,123, 10, 32,109, 97,114,103,
-     105,110, 58, 32, 48, 32, 48, 32, 48, 32, 49, 49, 48,112,120, 59, 10,
-      32,112, 97,100,100,105,110,103, 58, 32, 48, 32, 48, 32, 48, 46, 53,
-     101,109, 32, 48, 59, 10,125, 10, 10, 47, 42, 32,102,111,114, 32, 98,
-     114,111,119,115,101,114,115, 32,105,110, 32,115,116, 97,110,100, 97,
-     114,100,115, 32, 99,111,109,112,108,105, 97,110, 99,101, 32,109,111,
-     100,101, 32, 42, 47, 10,116,100, 32,112, 32,123, 10, 32, 32,109, 97,
-     114,103,105,110, 58, 32, 48, 59, 10,125, 10);
+     116, 32, 42, 47, 10,100,108, 32,123, 10, 32, 98,111,114,100,101,114,
+      58, 32, 51,112,120, 32,100,111,117, 98,108,101, 32, 35, 99, 99, 99,
+      59, 10, 32,112, 97,100,100,105,110,103, 58, 32, 48, 46, 53,101,109,
+      59, 10,125, 10, 10, 47, 42, 32,100,101,102,105,110,105,116,105,111,
+     110, 32,108,105,115,116, 58, 32,116,101,114,109, 32, 42, 47, 10,100,
+     116, 32,123, 10, 32,102,108,111, 97,116, 58, 32,108,101,102,116, 59,
+      10, 32, 99,108,101, 97,114, 58, 32,108,101,102,116, 59, 10, 32,119,
+     105,100,116,104, 58, 32, 97,117,116,111, 59, 32, 47, 42, 32,110,111,
+     114,109, 97,108,108,121, 32, 98,114,111,119,115,101,114,115, 32,100,
+     101,102, 97,117,108,116, 32,119,105,100,116,104, 32,111,102, 32,108,
+      97,114,103,101,115,116, 32,105,116,101,109, 32, 42, 47, 10, 32,112,
+      97,100,100,105,110,103, 45,114,105,103,104,116, 58, 32, 50, 48,112,
+     120, 59, 10, 32,102,111,110,116, 45,119,101,105,103,104,116, 58, 32,
+      98,111,108,100, 59, 10, 32, 99,111,108,111,114, 58, 32,100, 97,114,
+     107,103,114,101,101,110, 59, 10,125, 10, 10, 47, 42, 32,100,101,102,
+     105,110,105,116,105,111,110, 32,108,105,115,116, 58, 32,100,101,115,
+      99,114,105,112,116,105,111,110, 32, 42, 47, 10,100,100, 32,123, 10,
+      32,109, 97,114,103,105,110, 58, 32, 48, 32, 48, 32, 48, 32, 49, 49,
+      48,112,120, 59, 10, 32,112, 97,100,100,105,110,103, 58, 32, 48, 32,
+      48, 32, 48, 46, 53,101,109, 32, 48, 59, 10,125, 10, 10, 47, 42, 32,
+     102,111,114, 32, 98,114,111,119,115,101,114,115, 32,105,110, 32,115,
+     116, 97,110,100, 97,114,100,115, 32, 99,111,109,112,108,105, 97,110,
+      99,101, 32,109,111,100,101, 32, 42, 47, 10,116,100, 32,112, 32,123,
+      10, 32, 32,109, 97,114,103,105,110, 58, 32, 48, 59, 10,125, 10, 10,
+     115,112, 97,110, 46,116,111,103,103,108,101,116,114,101,101, 99,108,
+     111,115,101, 32,123, 10, 32, 32, 32, 32, 98, 97, 99,107,103,114,111,
+     117,110,100, 58, 32,117,114,108, 40,109,105,110,117,115, 46,112,110,
+     103, 41, 32, 99,101,110,116,101,114, 32,108,101,102,116, 32,110,111,
+      45,114,101,112,101, 97,116, 59, 10, 32, 32, 32, 32,112, 97,100,100,
+     105,110,103, 45,108,101,102,116, 58, 32, 50, 48,112,120, 59, 10,125,
+      10,115,112, 97,110, 46,116,111,103,103,108,101,116,114,101,101,111,
+     112,101,110, 32,123, 10, 32, 32, 32, 32, 98, 97, 99,107,103,114,111,
+     117,110,100, 58, 32,117,114,108, 40,112,108,117,115, 46,112,110,103,
+      41, 32, 99,101,110,116,101,114, 32,108,101,102,116, 32,110,111, 45,
+     114,101,112,101, 97,116, 59, 10, 32, 32, 32, 32,112, 97,100,100,105,
+     110,103, 45,108,101,102,116, 58, 32, 50, 48,112,120, 59, 10,125, 10,
+      10,117,108, 46, 99,108, 97,115,115,116,114,101,101,108,105,115,116,
+      32,108,105, 32,123, 32,112, 97,100,100,105,110,103, 45,108,101,102,
+     116, 58, 32, 48,112,120, 59, 32,125, 10, 10,117,108, 46, 99,108, 97,
+     115,115,116,114,101,101,108,105,115,116, 32,123, 32,108,105,115,116,
+      45,115,116,121,108,101, 45,116,121,112,101, 58,110,111,110,101, 59,
+      32,125, 10, 10,108,105, 46, 99,108, 97,115,115,116,114,101,101, 32,
+     117,108, 32,123, 32,100,105,115,112,108, 97,121, 58, 32, 98,108,111,
+      99,107, 59, 32,125, 10, 32, 10,108,105, 46, 99,108, 97,115,115,116,
+     114,101,101, 99,108,111,115,101,100, 32,117,108, 32,123, 32,100,105,
+     115,112,108, 97,121, 58, 32,110,111,110,101, 59, 32,125, 10);

+ 4 - 8
utils/fpdoc/dglobals.pp

@@ -144,6 +144,8 @@ resourcestring
   SCopyright2      = '(c) 2005 - 2012 various FPC contributors';
   SCopyright2      = '(c) 2005 - 2012 various FPC contributors';
 
 
   SCmdLineHelp     = 'Usage: %s [options]';
   SCmdLineHelp     = 'Usage: %s [options]';
+  SUsageOption008  = '--base-descr-dir=DIR prefix all description files with this directory';
+  SUsageOption009  = '--base-input-dir=DIR prefix all input files with this directory';
   SUsageOption010  = '--content         Create content file for package cross-references';
   SUsageOption010  = '--content         Create content file for package cross-references';
   SUsageOption020  = '--cputarget=value Set the target CPU for the scanner.';
   SUsageOption020  = '--cputarget=value Set the target CPU for the scanner.';
   SUsageOption030  = '--descr=file      use file as description file, e.g.: ';
   SUsageOption030  = '--descr=file      use file as description file, e.g.: ';
@@ -711,9 +713,9 @@ var
     end;
     end;
   end;
   end;
 
 
-  function ResolvePackageModule(AName:String;var pkg:TPasPackage;var module:TPasModule;createnew:boolean):String;
+  function ResolvePackageModule(AName:String;out pkg:TPasPackage;out module:TPasModule;createnew:boolean):String;
     var
     var
-      DotPos, DotPos2, i,j: Integer;
+      DotPos, DotPos2, i: Integer;
       s: String;
       s: String;
       HPackage: TPasPackage;
       HPackage: TPasPackage;
 
 
@@ -809,7 +811,6 @@ var
 
 
     function CreateClass(const AName: String;InheritanceStr:String): TPasClassType;
     function CreateClass(const AName: String;InheritanceStr:String): TPasClassType;
     var
     var
-      DotPos, DotPos2, i,j: Integer;
       s: String;
       s: String;
       HPackage: TPasPackage;
       HPackage: TPasPackage;
       Module: TPasModule;
       Module: TPasModule;
@@ -1446,9 +1447,7 @@ Var
   end;
   end;
 
 
 var
 var
-  i: Integer;
   Node, Subnode, Subsubnode: TDOMNode;
   Node, Subnode, Subsubnode: TDOMNode;
-  Element: TDOMElement;
   Doc: TXMLDocument;
   Doc: TXMLDocument;
   PackageDocNode, TopicNode,ModuleDocNode: TDocNode;
   PackageDocNode, TopicNode,ModuleDocNode: TDocNode;
 
 
@@ -1601,9 +1600,6 @@ end;
 
 
 function TFPDocEngine.FindLinkedNode(ANode : TDocNode) : TDocNode;
 function TFPDocEngine.FindLinkedNode(ANode : TDocNode) : TDocNode;
 
 
-Var
-  S: String;
-
 begin
 begin
   If (ANode.Link='') then
   If (ANode.Link='') then
     Result:=Nil
     Result:=Nil

+ 1 - 1
utils/fpdoc/dw_html.pp

@@ -2471,7 +2471,7 @@ begin
     try
     try
       B.BuildTree(AList);
       B.BuildTree(AList);
       // Classes
       // Classes
-      WriteXMLFile(B.ClassTree,'tree.xml');
+      // WriteXMLFile(B.ClassTree,'tree.xml');
       // Dummy TObject
       // Dummy TObject
       E:=B.ClassTree.DocumentElement;
       E:=B.ClassTree.DocumentElement;
       PushClassList;
       PushClassList;

+ 11 - 0
utils/fpdoc/examples/basedir/readme.txt

@@ -0,0 +1,11 @@
+This directory demonstrates the use of a fpdoc project file.
+It uses the files in the examples/simple directory.
+
+The project file contains the names of the files without paths.
+That means that fpdoc must be executed from this directory, 
+supplying the paths to the input and description files
+
+fpdoc --project=sample-project.xml --base-input-dir=../simple --base-descr-dir=../simple
+
+The docs will be written to a subdirectory doc. 
+This directory can be deleted if it is no longer necessary.

+ 29 - 0
utils/fpdoc/examples/basedir/sample-project.xml

@@ -0,0 +1,29 @@
+<docproject>
+  <packages>
+    <!-- Multiple packages can be entered. 
+         If only one is specified, it is selected. 
+         "name" is a mandatory attribute
+         a "units" tag is required, and a "descriptions" tag as well
+    -->
+    <package name="fpdocsample" output="doc" contentfile="fpdocsample.cnt">
+      <!-- All input files, one "unit" tag per unit -->
+      <units>
+        <!-- "file" is a mandatory attribute, "options" is not mandatory -->
+        <unit file="testunit.pp" options="-S2"/>
+      </units>
+      <descriptions>
+        <!-- Description files here. One "description" tag per file.
+            "file" is the only mandatory attribute -->
+        <description file="testunit.xml"/>
+      </descriptions>
+    </package>
+  </packages>
+  <options>
+    <!-- All command-line options can be specified here with the same name
+         and value as on the actual command-line. Boolean options must have
+         a value of 'true', '1' or 'yes' -->
+    <option name="format" value="html"/>
+    <option name="hide-protected" value="true"/>
+    <option name="footer-date" value="yyyy-mm-dd"/>
+  </options>
+</docproject>

+ 0 - 0
utils/fpdoc/gentest.sh → utils/fpdoc/examples/gentest.sh


+ 10 - 0
utils/fpdoc/examples/project/readme.txt

@@ -0,0 +1,10 @@
+This directory demonstrates the use of a fpdoc project file.
+It uses the files in the examples/simple directory.
+
+The project file contains the names of the files with relative paths.
+That means that fpdoc must be executed from this directory:
+
+fpdoc --project=sample-project.xml
+
+The docs will be written to a subdirectory doc. 
+This directory can be deleted if it is no longer necessary.

+ 29 - 0
utils/fpdoc/examples/project/sample-project.xml

@@ -0,0 +1,29 @@
+<docproject>
+  <packages>
+    <!-- Multiple packages can be entered. 
+         If only one is specified, it is selected. 
+         "name" is a mandatory attribute
+         a "units" tag is required, and a "descriptions" tag as well
+    -->
+    <package name="fpdocsample" output="doc" contentfile="fpdocsample.cnt">
+      <!-- All input files, one "unit" tag per unit -->
+      <units>
+        <!-- "file" is a mandatory attribute, "options" is not mandatory -->
+        <unit file="../simple/testunit.pp" options="-S2"/>
+      </units>
+      <descriptions>
+        <!-- Description files here. One "description" tag per file.
+            "file" is the only mandatory attribute -->
+        <description file="../simple/testunit.xml"/>
+      </descriptions>
+    </package>
+  </packages>
+  <options>
+    <!-- All command-line options can be specified here with the same name
+         and value as on the actual command-line. Boolean options must have
+         a value of 'true', '1' or 'yes' -->
+    <option name="format" value="html"/>
+    <option name="hide-protected" value="true"/>
+    <option name="footer-date" value="yyyy-mm-dd"/>
+  </options>
+</docproject>

+ 2 - 0
utils/fpdoc/examples/simple/html.bat

@@ -0,0 +1,2 @@
+rem Command line to create html docs.
+fpdoc --package=fpdocsample --output=doc --format=html --input="-S2 testunit.pp" --descr=testunit.xml

+ 2 - 0
utils/fpdoc/examples/simple/html.sh

@@ -0,0 +1,2 @@
+#!/bin/sh
+fpdoc --package=fpdocsample --output=doc --format=html --input='-S2 testunit.pp' --descr=testunit.xml

+ 9 - 0
utils/fpdoc/examples/simple/readme.txt

@@ -0,0 +1,9 @@
+This directory contains the files for the projects.
+
+You can create HTML documentation using just the command-line by executing the
+following command in this directory:
+
+(on 1 line)
+fpdoc --package=fpdocsample --output=doc --format=html --input='-S2 testunit.pp' --descr=testunit.xml
+
+Sample command-lines can be found in html.sh and html.bat

+ 0 - 0
utils/fpdoc/testunit.pp → utils/fpdoc/examples/simple/testunit.pp


+ 0 - 0
utils/fpdoc/testunit.xml → utils/fpdoc/examples/simple/testunit.xml


+ 7 - 11
utils/fpdoc/fpclasschart.pp

@@ -25,7 +25,6 @@ resourcestring
   STitle = 'fpClassTree - Create class tree from pascal sources';
   STitle = 'fpClassTree - Create class tree from pascal sources';
   SVersion = 'Version %s [%s]';
   SVersion = 'Version %s [%s]';
   SCopyright = '(c) 2008 - Michael Van Canneyt, [email protected]';
   SCopyright = '(c) 2008 - Michael Van Canneyt, [email protected]';
-  SCmdLineHelp = 'See documentation for usage.';
   SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
   SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
   SDone = 'Done.';
   SDone = 'Done.';
   SSkipMerge = 'Cannot merge %s into %s tree.';
   SSkipMerge = 'Cannot merge %s into %s tree.';
@@ -213,7 +212,6 @@ end;
 procedure TClassChartFormatter.EmitClass(E : TDomElement; HasSiblings: Boolean);
 procedure TClassChartFormatter.EmitClass(E : TDomElement; HasSiblings: Boolean);
 
 
 Var
 Var
-  DidSub : Boolean;
   N : TDomNode;
   N : TDomNode;
   I : Integer;
   I : Integer;
   L : TFPList;
   L : TFPList;
@@ -235,7 +233,6 @@ begin
       end;
       end;
     DoEmitClass(E);
     DoEmitClass(E);
     N:=E.FirstChild;
     N:=E.FirstChild;
-    DidSub:=False;
     L:=TFPList.Create;
     L:=TFPList.Create;
     try
     try
       While (N<>Nil) do
       While (N<>Nil) do
@@ -432,8 +429,6 @@ function TClassTreeEngine.CreateElement(AClass: TPTreeElement; const AName: Stri
   AParent: TPasElement; AVisibility : TPasMemberVisibility;
   AParent: TPasElement; AVisibility : TPasMemberVisibility;
   const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
   const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
 
 
-Var
-  DN : TDocNode;
 
 
 begin
 begin
   Result := AClass.Create(AName, AParent);
   Result := AClass.Create(AName, AParent);
@@ -478,6 +473,7 @@ Var
 
 
 
 
 begin
 begin
+  Result:=0;
   N:=Source.FirstChild;
   N:=Source.FirstChild;
   While (N<>Nil) do
   While (N<>Nil) do
     begin
     begin
@@ -503,7 +499,6 @@ Function MergeTrees (Dest,Source : TXMLDocument) : Integer;
 
 
 Var
 Var
   S,D : TDomElement;
   S,D : TDomElement;
-  Count : Integer;
 
 
 begin
 begin
   Result:=0;
   Result:=0;
@@ -524,28 +519,30 @@ Var
   Engine: TClassTreeEngine;
   Engine: TClassTreeEngine;
 
 
 begin
 begin
+  Result:='';
+  ACount:=0;
   XML:=TXMLDocument.Create;
   XML:=TXMLDocument.Create;
   Try
   Try
     //XML.
     //XML.
-    XML.AppendChild(XML.CreateElement(ObjKindNames[AObjectKind]));
+    XML.AppendChild(XML.CreateElement('TObject'));
     For I:=0 to MergeFiles.Count-1 do
     For I:=0 to MergeFiles.Count-1 do
       begin
       begin
       XMl2:=TXMLDocument.Create;
       XMl2:=TXMLDocument.Create;
       ReadXMLFile(XML2,MergeFiles[i]);
       ReadXMLFile(XML2,MergeFiles[i]);
       try
       try
-        ACount:=MergeTrees(XML,XML2);
+        ACount:=ACount+MergeTrees(XML,XML2);
         WriteLn(StdErr,Format(SMergedFile,[ACount,MergeFiles[i]]));
         WriteLn(StdErr,Format(SMergedFile,[ACount,MergeFiles[i]]));
       Finally
       Finally
         FreeAndNil(XML2);
         FreeAndNil(XML2);
       end;
       end;
       end;
       end;
-    ACount:=0;
     For I:=0 to InputFiles.Count-1 do
     For I:=0 to InputFiles.Count-1 do
       begin
       begin
       Engine := TClassTreeEngine.Create(XML,AObjectKind);
       Engine := TClassTreeEngine.Create(XML,AObjectKind);
       Try
       Try
         ParseSource(Engine,InputFiles[I],OSTarget,CPUTarget);
         ParseSource(Engine,InputFiles[I],OSTarget,CPUTarget);
-        ACount:=ACount+Engine.Ftree.BuildTree(Engine.FObjects);
+        Engine.Ftree.BuildTree(Engine.FObjects);
+        ACount:=ACount+MergeTrees(XML,Engine.FTree.ClassTree);
       Finally
       Finally
         FreeAndNil(Engine);
         FreeAndNil(Engine);
       end;
       end;
@@ -586,7 +583,6 @@ var
   InputFiles, 
   InputFiles, 
   MergeFiles : TStringList;
   MergeFiles : TStringList;
   DocLang : String;
   DocLang : String;
-  PackageName, 
   OutputName: String;
   OutputName: String;
 
 
 procedure InitOptions;
 procedure InitOptions;

+ 21 - 0
utils/fpdoc/fpdoc.css

@@ -127,6 +127,10 @@ table.bar {
   background-color: #a0c0ff;
   background-color: #a0c0ff;
 }
 }
 
 
+td p {
+ margin: 0;
+}
+
 span.bartitle {
 span.bartitle {
   font-weight: bold;
   font-weight: bold;
   font-style: italic;
   font-style: italic;
@@ -164,3 +168,20 @@ dd {
 td p {
 td p {
   margin: 0;
   margin: 0;
 }
 }
+
+span.toggletreeclose {
+    background: url(minus.png) center left no-repeat;
+    padding-left: 20px;
+}
+span.toggletreeopen {
+    background: url(plus.png) center left no-repeat;
+    padding-left: 20px;
+}
+
+ul.classtreelist li { padding-left: 0px; }
+
+ul.classtreelist { list-style-type:none; }
+
+li.classtree ul { display: block; }
+ 
+li.classtreeclosed ul { display: none; }

+ 6 - 0
utils/fpdoc/fpdoc.pp

@@ -73,6 +73,8 @@ Var
 
 
 begin
 begin
   Writeln(Format(SCmdLineHelp,[ExtractFileName(Paramstr(0))]));
   Writeln(Format(SCmdLineHelp,[ExtractFileName(Paramstr(0))]));
+  Writeln(SUsageOption008);
+  Writeln(SUsageOption009);
   Writeln(SUsageOption010);
   Writeln(SUsageOption010);
   Writeln(SUsageOption020);
   Writeln(SUsageOption020);
   Writeln(SUsageOption030);
   Writeln(SUsageOption030);
@@ -321,6 +323,8 @@ begin
       AddToFileList(SelectedPackage.Descriptions, Arg)
       AddToFileList(SelectedPackage.Descriptions, Arg)
     else if (Cmd = '--descr-dir') then
     else if (Cmd = '--descr-dir') then
       AddDirToFileList(SelectedPackage.Descriptions, Arg, '*.xml')
       AddDirToFileList(SelectedPackage.Descriptions, Arg, '*.xml')
+    else if (Cmd = '--base-descr-dir') then
+      FCreator.BaseDescrDir:=Arg
     else if (Cmd = '-f') or (Cmd = '--format') then
     else if (Cmd = '-f') or (Cmd = '--format') then
       begin
       begin
       Arg:=UpperCase(Arg);
       Arg:=UpperCase(Arg);
@@ -333,6 +337,8 @@ begin
       FCreator.Options.Language := Arg
       FCreator.Options.Language := Arg
     else if (Cmd = '-i') or (Cmd = '--input') then
     else if (Cmd = '-i') or (Cmd = '--input') then
       AddToFileList(SelectedPackage.Inputs, Arg)
       AddToFileList(SelectedPackage.Inputs, Arg)
+    else if (Cmd = '--base-input-dir') then
+      FCreator.BaseInputDir:=Arg
     else if (Cmd = '--input-dir') then
     else if (Cmd = '--input-dir') then
       begin
       begin
       AddDirToFileList(SelectedPackage.Inputs, Arg,'*.pp');
       AddDirToFileList(SelectedPackage.Inputs, Arg,'*.pp');

+ 5 - 11
utils/fpdoc/fpdocclasstree.pp

@@ -30,8 +30,7 @@ implementation
 
 
 constructor TClassTreeBuilder.Create(APackage : TPasPackage;
 constructor TClassTreeBuilder.Create(APackage : TPasPackage;
   AObjectKind: TPasObjKind);
   AObjectKind: TPasObjKind);
-Var
-  N : TDomNode;
+
 begin
 begin
   FCLassTree:=TXMLDocument.Create;
   FCLassTree:=TXMLDocument.Create;
   FPackage:=APAckage;
   FPackage:=APAckage;
@@ -82,7 +81,7 @@ begin
     S:=N.NodeName;
     S:=N.NodeName;
     if NoPath then
     if NoPath then
       Begin
       Begin
-      Result:= (CompareText(S,AElement.Name)=0);
+      Result:=(CompareText(S,AElement.Name)=0);
       end
       end
     else
     else
       begin
       begin
@@ -132,10 +131,11 @@ Var
   N : TDomNode;
   N : TDomNode;
 
 
 begin
 begin
-  //Writeln('Enter TClassTreeBuilder.AddToClassTree');
+
+//  Writeln('Enter TClassTreeBuilder.AddToClassTree');
   //if Assigned(AElement) then
   //if Assigned(AElement) then
     //Writeln('Addtoclasstree : ',aElement.Name);
     //Writeln('Addtoclasstree : ',aElement.Name);
-  Result:=Nil; N:=Nil;PE:=NIL;
+  Result:=Nil; M:=Nil; N:=Nil;PE:=NIL;PC:=Nil;
   If (AElement=Nil) then
   If (AElement=Nil) then
     begin
     begin
     Result:=FTreeStart;
     Result:=FTreeStart;
@@ -145,9 +145,7 @@ begin
     begin
     begin
     N:=LookForElement(FTreeStart,AElement,True);
     N:=LookForElement(FTreeStart,AElement,True);
     If (N=Nil) then
     If (N=Nil) then
-      begin
       PE:=FTreeStart;
       PE:=FTreeStart;
-      end
     end
     end
   else If (AElement is TPasClassType) then
   else If (AElement is TPasClassType) then
     begin
     begin
@@ -164,8 +162,6 @@ begin
     end;
     end;
   If (N<>Nil) then
   If (N<>Nil) then
     begin
     begin
-//    if Assigned(PC) then
-//      Writeln(PC.Name,' already in tree');
     Result:=N as TDomElement
     Result:=N as TDomElement
    end
    end
   else if AElement.Name<>'' then
   else if AElement.Name<>'' then
@@ -180,10 +176,8 @@ begin
       end;
       end;
     if PE=Nil then
     if PE=Nil then
       begin
       begin
-      //Writeln('PE = nil detected for ',AElement.PathName);
       PE:=FTreeStart
       PE:=FTreeStart
       end;
       end;
-    //Writeln('Appending to ',PE.NodeName);
     // if not assigned, probably needs to be assigned to something else.
     // if not assigned, probably needs to be assigned to something else.
     if assigned(PE) then
     if assigned(PE) then
       PE.AppendChild(Result);
       PE.AppendChild(Result);

+ 55 - 12
utils/fpdoc/mkfpdoc.pp

@@ -19,6 +19,8 @@ Type
 
 
   TFPDocCreator = Class(TComponent)
   TFPDocCreator = Class(TComponent)
   Private
   Private
+    FBaseDescrDir: String;
+    FBaseInputDir: String;
     FCurPackage : TFPDocPackage;
     FCurPackage : TFPDocPackage;
     FProcessedUnits : TStrings;
     FProcessedUnits : TStrings;
     FOnLog: TPasParserLogHandler;
     FOnLog: TPasParserLogHandler;
@@ -28,7 +30,11 @@ Type
     FVerbose: Boolean;
     FVerbose: Boolean;
     function GetOptions: TEngineOptions;
     function GetOptions: TEngineOptions;
     function GetPackages: TFPDocPackages;
     function GetPackages: TFPDocPackages;
+    procedure SetBaseDescrDir(AValue: String);
+    procedure SetBaseInputDir(AValue: String);
   Protected
   Protected
+    Function FixInputFile(Const AFileName : String) : String;
+    Function FixDescrFile(Const AFileName : String) : String;
     Procedure DoBeforeEmitNote(Sender : TObject; Note : TDomElement; Var EmitNote : Boolean); virtual;
     Procedure DoBeforeEmitNote(Sender : TObject; Note : TDomElement; Var EmitNote : Boolean); virtual;
     procedure HandleOnParseUnit(Sender: TObject; const AUnitName: String; out AInputFile, OSTarget, CPUTarget: String);
     procedure HandleOnParseUnit(Sender: TObject; const AUnitName: String; out AInputFile, OSTarget, CPUTarget: String);
     procedure SetVerbose(AValue: Boolean); virtual;
     procedure SetVerbose(AValue: Boolean); virtual;
@@ -49,6 +55,9 @@ Type
     // Easy access
     // Easy access
     Property Options : TEngineOptions Read GetOptions;
     Property Options : TEngineOptions Read GetOptions;
     Property Packages : TFPDocPackages Read GetPackages;
     Property Packages : TFPDocPackages Read GetPackages;
+    // When set, they will be prepended to non-absolute filenames.
+    Property BaseInputDir : String Read FBaseInputDir Write SetBaseInputDir;
+    Property BaseDescrDir : String Read FBaseDescrDir Write SetBaseDescrDir;
   end;
   end;
 
 
 implementation
 implementation
@@ -72,13 +81,13 @@ begin
     end;
     end;
 end;
 end;
 
 
-procedure TFPDocCreator.DoLog(const Msg: String);
+Procedure TFPDocCreator.DoLog(Const Msg: String);
 begin
 begin
   If Assigned(OnLog) then
   If Assigned(OnLog) then
     OnLog(Self,Msg);
     OnLog(Self,Msg);
 end;
 end;
 
 
-procedure TFPDocCreator.DoLog(const Fmt: String; Args: array of const);
+procedure TFPDocCreator.DoLog(Const Fmt: String; Args: Array of Const);
 begin
 begin
   DoLog(Format(Fmt,Args));
   DoLog(Format(Fmt,Args));
 end;
 end;
@@ -103,7 +112,7 @@ begin
        SplitInputFIleOption(S,UN,Opts);
        SplitInputFIleOption(S,UN,Opts);
        if CompareText(ChangeFileExt(ExtractFileName(Un),''),AUnitName)=0 then
        if CompareText(ChangeFileExt(ExtractFileName(Un),''),AUnitName)=0 then
          begin
          begin
-         AInputFile:=S;
+         AInputFile:=FixInputFile(UN)+' '+Opts;
          OSTarget:=FProject.Options.OSTarget;
          OSTarget:=FProject.Options.OSTarget;
          CPUTarget:=FProject.Options.CPUTarget;
          CPUTarget:=FProject.Options.CPUTarget;
          FProcessedUnits.Add(UN);
          FProcessedUnits.Add(UN);
@@ -123,13 +132,45 @@ begin
   Result:=FProject.Packages;
   Result:=FProject.Packages;
 end;
 end;
 
 
-procedure TFPDocCreator.DoBeforeEmitNote(Sender: TObject; Note: TDomElement;
-  var EmitNote: Boolean);
+Function TFPDocCreator.FixInputFile(Const AFileName: String): String;
+begin
+  Result:=AFileName;
+  If Result='' then exit;
+  if (ExtractFileDrive(Result)='') and (Result[1]<>PathDelim) then
+    Result:=BaseInputDir+Result;
+end;
+
+Function TFPDocCreator.FixDescrFile(Const AFileName: String): String;
+begin
+  Result:=AFileName;
+  If Result='' then exit;
+  if (ExtractFileDrive(Result)='') and (Result[1]<>PathDelim) then
+    Result:=BaseDescrDir+Result;
+end;
+
+procedure TFPDocCreator.SetBaseDescrDir(AValue: String);
+begin
+  if FBaseDescrDir=AValue then Exit;
+  FBaseDescrDir:=AValue;
+  If FBaseDescrDir<>'' then
+    FBaseDescrDir:=IncludeTrailingPathDelimiter(FBaseDescrDir);
+end;
+
+procedure TFPDocCreator.SetBaseInputDir(AValue: String);
+begin
+  if FBaseInputDir=AValue then Exit;
+  FBaseInputDir:=AValue;
+  If FBaseInputDir<>'' then
+    FBaseInputDir:=IncludeTrailingPathDelimiter(FBaseInputDir);
+end;
+
+Procedure TFPDocCreator.DoBeforeEmitNote(Sender: TObject; Note: TDomElement;
+  Var EmitNote: Boolean);
 begin
 begin
   EmitNote:=True;
   EmitNote:=True;
 end;
 end;
 
 
-constructor TFPDocCreator.Create(AOwner: TComponent);
+Constructor TFPDocCreator.Create(AOwner: TComponent);
 begin
 begin
   inherited Create(AOwner);
   inherited Create(AOwner);
   FProject:=TFPDocProject.Create(Self);
   FProject:=TFPDocProject.Create(Self);
@@ -139,7 +180,7 @@ begin
   FProcessedUnits:=TStringList.Create;
   FProcessedUnits:=TStringList.Create;
 end;
 end;
 
 
-destructor TFPDocCreator.Destroy;
+Destructor TFPDocCreator.Destroy;
 begin
 begin
   FreeAndNil(FProcessedUnits);
   FreeAndNil(FProcessedUnits);
   FreeAndNil(FProject);
   FreeAndNil(FProject);
@@ -180,7 +221,8 @@ begin
     Engine.WriteContentFile(APackage.ContentFile);
     Engine.WriteContentFile(APackage.ContentFile);
 end;
 end;
 
 
-procedure TFPDocCreator.CreateDocumentation(APackage: TFPDocPackage; ParseOnly : Boolean);
+Procedure TFPDocCreator.CreateDocumentation(APackage: TFPDocPackage;
+  ParseOnly: Boolean);
 
 
 var
 var
   i,j: Integer;
   i,j: Integer;
@@ -201,7 +243,7 @@ begin
       Engine.ReadContentFile(Arg, Cmd);
       Engine.ReadContentFile(Arg, Cmd);
       end;
       end;
     for i := 0 to APackage.Descriptions.Count - 1 do
     for i := 0 to APackage.Descriptions.Count - 1 do
-      Engine.AddDocFile(APackage.Descriptions[i],Options.donttrim);
+      Engine.AddDocFile(FixDescrFile(APackage.Descriptions[i]),Options.donttrim);
     Engine.SetPackageName(APackage.Name);
     Engine.SetPackageName(APackage.Name);
     Engine.Output:=APackage.Output;
     Engine.Output:=APackage.Output;
     Engine.OnLog:=Self.OnLog;
     Engine.OnLog:=Self.OnLog;
@@ -216,10 +258,11 @@ begin
     for i := 0 to APackage.Inputs.Count - 1 do
     for i := 0 to APackage.Inputs.Count - 1 do
       try
       try
         SplitInputFileOption(APackage.Inputs[i],Cmd,Arg);
         SplitInputFileOption(APackage.Inputs[i],Cmd,Arg);
+        Cmd:=FixInputFIle(Cmd);
         if FProcessedUnits.IndexOf(Cmd)=-1 then
         if FProcessedUnits.IndexOf(Cmd)=-1 then
           begin
           begin
           FProcessedUnits.Add(Cmd);
           FProcessedUnits.Add(Cmd);
-          ParseSource(Engine, APackage.Inputs[i], Options.OSTarget, Options.CPUTarget);
+          ParseSource(Engine,Cmd+' '+Arg, Options.OSTarget, Options.CPUTarget);
           end;
           end;
       except
       except
         on e: EParserError do
         on e: EParserError do
@@ -239,7 +282,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TFPDocCreator.CreateProjectFile(Const AFileName: string);
+Procedure TFPDocCreator.CreateProjectFile(Const AFileName: string);
 begin
 begin
   With TXMLFPDocOptions.Create(Self) do
   With TXMLFPDocOptions.Create(Self) do
   try
   try
@@ -249,7 +292,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TFPDocCreator.LoadProjectFile(const AFileName: string);
+Procedure TFPDocCreator.LoadProjectFile(Const AFileName: string);
 begin
 begin
   With TXMLFPDocOptions.Create(self) do
   With TXMLFPDocOptions.Create(self) do
     try
     try