浏览代码

* Patch from Mattias Gaertner: Record support, detect duplicate identifiers, bug fixes

git-svn-id: trunk@34520 -
michael 9 年之前
父节点
当前提交
a55c176bef

+ 1 - 0
.gitattributes

@@ -6627,6 +6627,7 @@ packages/pastojs/Makefile.fpc svneol=native#text/plain
 packages/pastojs/fpmake.pp svneol=native#text/plain
 packages/pastojs/fpmake.pp svneol=native#text/plain
 packages/pastojs/src/fppas2js.pp svneol=native#text/plain
 packages/pastojs/src/fppas2js.pp svneol=native#text/plain
 packages/pastojs/tests/tcconverter.pp svneol=native#text/plain
 packages/pastojs/tests/tcconverter.pp svneol=native#text/plain
+packages/pastojs/tests/tcmodules.pas svneol=native#text/plain
 packages/pastojs/tests/testpas2js.lpi svneol=native#text/plain
 packages/pastojs/tests/testpas2js.lpi svneol=native#text/plain
 packages/pastojs/tests/testpas2js.pp svneol=native#text/plain
 packages/pastojs/tests/testpas2js.pp svneol=native#text/plain
 packages/pastojs/todo.txt svneol=native#text/plain
 packages/pastojs/todo.txt svneol=native#text/plain

+ 1 - 1
packages/fcl-js/src/jstree.pp

@@ -120,7 +120,7 @@ Type
     Constructor Create;
     Constructor Create;
     Destructor Destroy; override;
     Destructor Destroy; override;
     Property Params : TStrings Read FParams Write SetParams;
     Property Params : TStrings Read FParams Write SetParams;
-    Property Body : TJSFunctionBody Read FBody Write FBody;
+    Property Body : TJSFunctionBody Read FBody Write FBody; // can be nil
     Property Name : TJSString Read FName Write FName;
     Property Name : TJSString Read FName Write FName;
     Property IsEmpty : Boolean Read FIsEmpty Write FIsEmpty;
     Property IsEmpty : Boolean Read FIsEmpty Write FIsEmpty;
   end;
   end;

+ 18 - 6
packages/fcl-js/src/jswriter.pp

@@ -474,15 +474,17 @@ begin
   if Not (C or FD.IsEmpty) then
   if Not (C or FD.IsEmpty) then
     begin
     begin
     Writeln('');
     Writeln('');
-    indent;
+    Indent;
     end;
     end;
   if Assigned(FD.Body) then
   if Assigned(FD.Body) then
     begin
     begin
     FSkipBrackets:=True;
     FSkipBrackets:=True;
+    //writeln('TJSWriter.WriteFuncDef '+FD.Body.ClassName);
     WriteJS(FD.Body);
     WriteJS(FD.Body);
     If (Assigned(FD.Body.A))
     If (Assigned(FD.Body.A))
     and (not (FD.Body.A is TJSStatementList))
     and (not (FD.Body.A is TJSStatementList))
     and (not (FD.Body.A is TJSSourceElements))
     and (not (FD.Body.A is TJSSourceElements))
+    and (not (FD.Body.A is TJSEmptyBlockStatement))
     then
     then
       if C then
       if C then
         Write('; ')
         Write('; ')
@@ -493,7 +495,7 @@ begin
     Write('}')
     Write('}')
   else
   else
     begin
     begin
-    undent;
+    Undent;
     Write('}'); // do not writeln
     Write('}'); // do not writeln
     end;
     end;
 end;
 end;
@@ -697,14 +699,20 @@ Var
   LastEl: TJSElement;
   LastEl: TJSElement;
 
 
 begin
 begin
+  //write('TJSWriter.WriteStatementList '+BoolToStr(FSkipBrackets,true));
+  //if El.A<>nil then write(' El.A='+El.A.ClassName) else write(' El.A=nil');
+  //if El.B<>nil then write(' El.B='+El.B.ClassName) else write(' El.B=nil');
+  //writeln(' ');
+
   C:=(woCompact in Options);
   C:=(woCompact in Options);
   B:= Not FSkipBrackets;
   B:= Not FSkipBrackets;
   if B then
   if B then
     begin
     begin
     Write('{');
     Write('{');
+    Indent;
     if not C then writeln('');
     if not C then writeln('');
     end;
     end;
-  if Assigned(El.A) then
+  if Assigned(El.A) and (El.A.ClassType<>TJSEmptyBlockStatement) then
     begin
     begin
     WriteJS(El.A);
     WriteJS(El.A);
     LastEl:=El.A;
     LastEl:=El.A;
@@ -726,8 +734,8 @@ begin
     end;
     end;
   if B then
   if B then
     begin
     begin
-    Write('}');
-    if not C then writeln('');
+    Undent;
+    Write('}'); // do not writeln
     end;
     end;
 end;
 end;
 
 
@@ -865,7 +873,10 @@ begin
     begin
     begin
     Write('do ');
     Write('do ');
     if Assigned(El.Body) then
     if Assigned(El.Body) then
+      begin
+      FSkipBrackets:=false;
       WriteJS(El.Body);
       WriteJS(El.Body);
+      end;
     Write(' while (');
     Write(' while (');
     If Assigned(El.Cond) then
     If Assigned(El.Cond) then
       WriteJS(EL.Cond);
       WriteJS(EL.Cond);
@@ -1052,7 +1063,8 @@ end;
 procedure TJSWriter.WriteFunctionBody(El: TJSFunctionBody);
 procedure TJSWriter.WriteFunctionBody(El: TJSFunctionBody);
 
 
 begin
 begin
-  if Assigned(El.A) then
+  //writeln('TJSWriter.WriteFunctionBody '+El.A.ClassName+' FSkipBrackets='+BoolToStr(FSkipBrackets,'true','false'));
+  if Assigned(El.A) and (not (El.A is TJSEmptyBlockStatement)) then
     WriteJS(El.A);
     WriteJS(El.A);
 end;
 end;
 
 

+ 3 - 3
packages/fcl-js/tests/tcwriter.pp

@@ -1399,7 +1399,7 @@ Var
 begin
 begin
 //  Writer.Options:=[woCompact,woUseUTF8];
 //  Writer.Options:=[woCompact,woUseUTF8];
   S:=TJSStatementList.Create(0,0);
   S:=TJSStatementList.Create(0,0);
-  AssertWrite('Statement list','{'+sLineBreak+'}'+sLineBreak,S);
+  AssertWrite('Statement list','{'+sLineBreak+'}',S);
 end;
 end;
 
 
 Procedure TTestStatementWriter.TestStatementListEmptyCompact;
 Procedure TTestStatementWriter.TestStatementListEmptyCompact;
@@ -1420,7 +1420,7 @@ begin
 //  Writer.Options:=[woCompact,woUseUTF8];
 //  Writer.Options:=[woCompact,woUseUTF8];
   S:=TJSStatementList.Create(0,0);
   S:=TJSStatementList.Create(0,0);
   S.A:=CreateAssignment(nil);
   S.A:=CreateAssignment(nil);
-  AssertWrite('Statement list','{'+sLineBreak+'a = b;'+sLineBreak+'}'+sLineBreak,S);
+  AssertWrite('Statement list','{'+sLineBreak+'a = b;'+sLineBreak+'}',S);
 end;
 end;
 
 
 Procedure TTestStatementWriter.TestStatementListOneStatementCompact;
 Procedure TTestStatementWriter.TestStatementListOneStatementCompact;
@@ -1444,7 +1444,7 @@ begin
   S:=TJSStatementList.Create(0,0);
   S:=TJSStatementList.Create(0,0);
   S.A:=CreateAssignment(nil);
   S.A:=CreateAssignment(nil);
   S.B:=CreateAssignment(nil);
   S.B:=CreateAssignment(nil);
-  AssertWrite('Statement list','{'+sLineBreak+'a = b;'+sLineBreak+'a = b;'+sLineBreak+'}'+sLineBreak,S);
+  AssertWrite('Statement list','{'+sLineBreak+'a = b;'+sLineBreak+'a = b;'+sLineBreak+'}',S);
 end;
 end;
 
 
 Procedure TTestStatementWriter.TestStatementListTwoStatementsCompact;
 Procedure TTestStatementWriter.TestStatementListTwoStatementsCompact;

+ 4 - 1
packages/fcl-js/tests/testjs.lpr

@@ -3,7 +3,10 @@ program testjs;
 {$mode objfpc}{$H+}
 {$mode objfpc}{$H+}
 
 
 uses
 uses
-  cwstring,Classes, consoletestrunner, tcscanner, jsparser, jsscanner, jstree, jsbase,
+  {$IFDEF Unix}
+  cwstring,
+  {$ENDIF}
+  Classes, consoletestrunner, tcscanner, jsparser, jsscanner, jstree, jsbase,
   tcparser, jswriter, tcwriter, jstoken;
   tcparser, jswriter, tcwriter, jstoken;
 
 
 var
 var

+ 188 - 48
packages/fcl-passrc/src/pasresolver.pp

@@ -38,9 +38,12 @@
   - case of
   - case of
   - try..finally..except, on, else, raise
   - try..finally..except, on, else, raise
   - for loop
   - for loop
+  - spot duplicates
 
 
  ToDo:
  ToDo:
-  - spot duplicates
+   - records - TPasRecordType,
+     - variant - TPasVariant
+     - const  TRecordValues
   - check if types only refer types
   - check if types only refer types
   - nested forward procs, nested must be resolved before proc body
   - nested forward procs, nested must be resolved before proc body
   - program/library/implementation forward procs
   - program/library/implementation forward procs
@@ -49,9 +52,6 @@
   - enums - TPasEnumType, TPasEnumValue
   - enums - TPasEnumType, TPasEnumValue
     - propagate to parent scopes
     - propagate to parent scopes
   - ranges TPasRangeType
   - ranges TPasRangeType
-  - records - TPasRecordType,
-    - variant - TPasVariant
-    - const  TRecordValues
   - arrays  TPasArrayType
   - arrays  TPasArrayType
     - const TArrayValues
     - const TArrayValues
   - pointer TPasPointerType
   - pointer TPasPointerType
@@ -102,6 +102,7 @@ const
   nIncompatibleTypeArgNo = 3006;
   nIncompatibleTypeArgNo = 3006;
   nIncompatibleTypeArgNoVarParamMustMatchExactly = 3007;
   nIncompatibleTypeArgNoVarParamMustMatchExactly = 3007;
   nVariableIdentifierExpected = 3008;
   nVariableIdentifierExpected = 3008;
+  nDuplicateIdentifier = 3009;
 
 
 // resourcestring patterns of messages
 // resourcestring patterns of messages
 resourcestring
 resourcestring
@@ -113,6 +114,7 @@ resourcestring
   sIncompatibleTypeArgNo = 'Incompatible type arg no. %s: Got "%s", expected "%s"';
   sIncompatibleTypeArgNo = 'Incompatible type arg no. %s: Got "%s", expected "%s"';
   sIncompatibleTypeArgNoVarParamMustMatchExactly = 'Incompatible type arg no. %s: Got "%s", expected "%s". Var param must match exactly.';
   sIncompatibleTypeArgNoVarParamMustMatchExactly = 'Incompatible type arg no. %s: Got "%s", expected "%s". Var param must match exactly.';
   sVariableIdentifierExpected = 'Variable identifier expected';
   sVariableIdentifierExpected = 'Variable identifier expected';
+  sDuplicateIdentifier = 'Duplicate identifier "%s" at %s';
 
 
 type
 type
   TResolveBaseType = (
   TResolveBaseType = (
@@ -388,6 +390,11 @@ type
   TPasProcedureScope = Class(TPasIdentifierScope)
   TPasProcedureScope = Class(TPasIdentifierScope)
   end;
   end;
 
 
+  { TPasRecordScope }
+
+  TPasRecordScope = Class(TPasIdentifierScope)
+  end;
+
   { TPasExceptOnScope }
   { TPasExceptOnScope }
 
 
   TPasExceptOnScope = Class(TPasIdentifierScope)
   TPasExceptOnScope = Class(TPasIdentifierScope)
@@ -427,6 +434,17 @@ type
     property CurModule: TPasModule read FCurModule write SetCurModule;
     property CurModule: TPasModule read FCurModule write SetCurModule;
   end;
   end;
 
 
+  { TPasSubRecordScope }
+
+  TPasSubRecordScope = Class(TPasSubScope)
+  public
+    RecordScope: TPasRecordScope;
+    function FindIdentifier(const Identifier: String): TPasIdentifier; override;
+    procedure IterateElements(const aName: string;
+      const OnIterateElement: TIterateScopeElement; Data: Pointer;
+      var Abort: boolean); override;
+  end;
+
   TPasResolvedKind = (
   TPasResolvedKind = (
     rkNone,
     rkNone,
     rkIdentifier, // IdentEl is a type, var, const, property, proc, etc, built-in types have IdentEl=nil
     rkIdentifier, // IdentEl is a type, var, const, property, proc, etc, built-in types have IdentEl=nil
@@ -492,18 +510,23 @@ type
   protected
   protected
     procedure SetCurrentParser(AValue: TPasParser); override;
     procedure SetCurrentParser(AValue: TPasParser); override;
     procedure CheckTopScope(ExpectedClass: TPasScopeClass);
     procedure CheckTopScope(ExpectedClass: TPasScopeClass);
+    function AddIdentifier(Scope: TPasIdentifierScope;
+      const aName: String; El: TPasElement;
+      const Kind: TPasIdentifierKind): TPasIdentifier; virtual;
     procedure AddModule(El: TPasModule);
     procedure AddModule(El: TPasModule);
     procedure AddSection(El: TPasSection);
     procedure AddSection(El: TPasSection);
     procedure AddType(El: TPasType);
     procedure AddType(El: TPasType);
+    Procedure AddRecordType(El: TPasRecordType);
     procedure AddVariable(El: TPasVariable);
     procedure AddVariable(El: TPasVariable);
     procedure AddProcedure(El: TPasProcedure);
     procedure AddProcedure(El: TPasProcedure);
     procedure AddArgument(El: TPasArgument);
     procedure AddArgument(El: TPasArgument);
     procedure AddFunctionResult(El: TPasResultElement);
     procedure AddFunctionResult(El: TPasResultElement);
     procedure AddExceptOn(El: TPasImplExceptOn);
     procedure AddExceptOn(El: TPasImplExceptOn);
     procedure StartProcedureBody(El: TProcedureBody);
     procedure StartProcedureBody(El: TProcedureBody);
-    procedure FinishModule;
+    procedure FinishModule(CurModule: TPasModule);
     procedure FinishUsesList;
     procedure FinishUsesList;
     procedure FinishTypeSection;
     procedure FinishTypeSection;
+    procedure FinishTypeDef(El: TPasType);
     procedure FinishProcedure;
     procedure FinishProcedure;
     procedure FinishProcedureHeader;
     procedure FinishProcedureHeader;
     procedure FinishExceptOnExpr;
     procedure FinishExceptOnExpr;
@@ -534,9 +557,10 @@ type
     procedure IterateElements(const aName: string;
     procedure IterateElements(const aName: string;
       const OnIterateElement: TIterateScopeElement; Data: Pointer;
       const OnIterateElement: TIterateScopeElement; Data: Pointer;
       var Abort: boolean); virtual;
       var Abort: boolean); virtual;
-    procedure FinishScope(ScopeType: TPasScopeType); override;
+    procedure FinishScope(ScopeType: TPasScopeType; El: TPasElement); override;
     class procedure UnmangleSourceLineNumber(LineNumber: integer;
     class procedure UnmangleSourceLineNumber(LineNumber: integer;
       out Line, Column: integer);
       out Line, Column: integer);
+    class function GetElementSourcePosStr(El: TPasElement): string;
     procedure Clear; virtual;
     procedure Clear; virtual;
     procedure AddObjFPCBuiltInIdentifiers(BaseTypes: TResolveBaseTypes = btAllStandardTypes);
     procedure AddObjFPCBuiltInIdentifiers(BaseTypes: TResolveBaseTypes = btAllStandardTypes);
     function CreateReference(DeclEl, RefEl: TPasElement): TResolvedReference; virtual;
     function CreateReference(DeclEl, RefEl: TPasElement): TResolvedReference; virtual;
@@ -810,6 +834,21 @@ begin
   ResolvedType.ExprEl:=ExprEl;
   ResolvedType.ExprEl:=ExprEl;
 end;
 end;
 
 
+{ TPasSubRecordScope }
+
+function TPasSubRecordScope.FindIdentifier(const Identifier: String
+  ): TPasIdentifier;
+begin
+  Result:=RecordScope.FindIdentifier(Identifier);
+end;
+
+procedure TPasSubRecordScope.IterateElements(const aName: string;
+  const OnIterateElement: TIterateScopeElement; Data: Pointer;
+  var Abort: boolean);
+begin
+  RecordScope.IterateElements(aName, OnIterateElement, Data, Abort);
+end;
+
 { TPasIdentifier }
 { TPasIdentifier }
 
 
 procedure TPasIdentifier.SetElement(AValue: TPasElement);
 procedure TPasIdentifier.SetElement(AValue: TPasElement);
@@ -1135,12 +1174,26 @@ begin
     RaiseInternalError('Expected TopScope='+ExpectedClass.ClassName+' but found '+TopScope.ClassName);
     RaiseInternalError('Expected TopScope='+ExpectedClass.ClassName+' but found '+TopScope.ClassName);
 end;
 end;
 
 
-procedure TPasResolver.FinishModule;
+function TPasResolver.AddIdentifier(Scope: TPasIdentifierScope;
+  const aName: String; El: TPasElement; const Kind: TPasIdentifierKind
+  ): TPasIdentifier;
+var
+  Identifier, OlderIdentifier: TPasIdentifier;
+begin
+  Identifier:=Scope.AddIdentifier(aName,El,Kind);
+  OlderIdentifier:=Identifier.NextSameIdentifier;
+  // check duplicate
+  if OlderIdentifier<>nil then
+    if (Identifier.Kind=pikSimple) or (OlderIdentifier.Kind=pikSimple) then
+      RaiseMsg(nDuplicateIdentifier,sDuplicateIdentifier,
+               [aName,GetElementSourcePosStr(OlderIdentifier.Element)],El);
+  Result:=Identifier;
+end;
+
+procedure TPasResolver.FinishModule(CurModule: TPasModule);
 var
 var
   CurModuleClass: TClass;
   CurModuleClass: TClass;
-  CurModule: TPasModule;
 begin
 begin
-  CurModule:=CurrentParser.CurModule;
   {$IFDEF VerbosePasResolver}
   {$IFDEF VerbosePasResolver}
   writeln('TPasResolver.FinishModule START ',CurModule.Name);
   writeln('TPasResolver.FinishModule START ',CurModule.Name);
   {$ENDIF}
   {$ENDIF}
@@ -1199,7 +1252,7 @@ begin
     if (El.ClassType=TProgramSection) then
     if (El.ClassType=TProgramSection) then
       RaiseInternalError('used unit is a program: '+GetObjName(El));
       RaiseInternalError('used unit is a program: '+GetObjName(El));
 
 
-    Scope.AddIdentifier(El.Name,El,pikSimple);
+    AddIdentifier(Scope,El.Name,El,pikSimple);
 
 
     // check used unit
     // check used unit
     PublicEl:=nil;
     PublicEl:=nil;
@@ -1226,6 +1279,18 @@ begin
   // ToDo: resolve pending forwards
   // ToDo: resolve pending forwards
 end;
 end;
 
 
+procedure TPasResolver.FinishTypeDef(El: TPasType);
+begin
+  {$IFDEF VerbosePasResolver}
+  writeln('TPasResolver.FinishTypeDef El=',GetObjName(El));
+  {$ENDIF}
+  if TopScope.Element=El then
+    begin
+    if TopScope.ClassType=TPasRecordScope then
+      PopScope;
+    end;
+end;
+
 procedure TPasResolver.FinishProcedure;
 procedure TPasResolver.FinishProcedure;
 var
 var
   aProc: TPasProcedure;
   aProc: TPasProcedure;
@@ -1244,6 +1309,7 @@ procedure TPasResolver.FinishProcedureHeader;
 begin
 begin
   CheckTopScope(TPasProcedureScope);
   CheckTopScope(TPasProcedureScope);
   // ToDo: check class
   // ToDo: check class
+  // ToDo: check duplicate
 end;
 end;
 
 
 procedure TPasResolver.FinishExceptOnExpr;
 procedure TPasResolver.FinishExceptOnExpr;
@@ -1260,7 +1326,7 @@ begin
     Expr:=TPrimitiveExpr(El.VarExpr);
     Expr:=TPrimitiveExpr(El.VarExpr);
     if Expr.Kind<>pekIdent then
     if Expr.Kind<>pekIdent then
       RaiseNotYetImplemented(Expr);
       RaiseNotYetImplemented(Expr);
-    TPasExceptOnScope(FTopScope).AddIdentifier(Expr.Value,Expr,pikSimple);
+    AddIdentifier(TPasExceptOnScope(FTopScope),Expr.Value,Expr,pikSimple);
     end;
     end;
   if El.TypeExpr<>nil then
   if El.TypeExpr<>nil then
     ResolveExpr(El.TypeExpr);
     ResolveExpr(El.TypeExpr);
@@ -1287,6 +1353,8 @@ procedure TPasResolver.ResolveImplElement(El: TPasImplElement);
 begin
 begin
   //writeln('TPasResolver.ResolveImplElement ',GetObjName(El));
   //writeln('TPasResolver.ResolveImplElement ',GetObjName(El));
   if El=nil then
   if El=nil then
+  else if El.ClassType=TPasImplBeginBlock then
+    ResolveImplBlock(TPasImplBeginBlock(El))
   else if El.ClassType=TPasImplAssign then
   else if El.ClassType=TPasImplAssign then
     begin
     begin
     ResolveExpr(TPasImplAssign(El).left);
     ResolveExpr(TPasImplAssign(El).left);
@@ -1334,10 +1402,11 @@ begin
   else if El.ClassType=TPasImplCommand then
   else if El.ClassType=TPasImplCommand then
     begin
     begin
     if TPasImplCommand(El).Command<>'' then
     if TPasImplCommand(El).Command<>'' then
-      RaiseNotYetImplemented(El);
+      RaiseNotYetImplemented(El,'TPasResolver.ResolveImplElement');
     end
     end
+  else if El.ClassType=TPasImplAsmStatement then
   else
   else
-    RaiseNotYetImplemented(El);
+    RaiseNotYetImplemented(El,'TPasResolver.ResolveImplElement');
 end;
 end;
 
 
 procedure TPasResolver.ResolveImplCaseOf(CaseOf: TPasImplCaseOf);
 procedure TPasResolver.ResolveImplCaseOf(CaseOf: TPasImplCaseOf);
@@ -1423,6 +1492,7 @@ end;
 
 
 procedure TPasResolver.ResolveBinaryExpr(El: TBinaryExpr);
 procedure TPasResolver.ResolveBinaryExpr(El: TBinaryExpr);
 begin
 begin
+  //writeln('TPasResolver.ResolveBinaryExpr left=',GetObjName(El.left),' right=',GetObjName(El.right),' opcode=',OpcodeStrings[El.OpCode]);
   ResolveExpr(El.left);
   ResolveExpr(El.left);
   if El.right=nil then exit;
   if El.right=nil then exit;
   case El.OpCode of
   case El.OpCode of
@@ -1469,6 +1539,9 @@ var
   DeclEl: TPasElement;
   DeclEl: TPasElement;
   ModuleScope: TPasSubModuleScope;
   ModuleScope: TPasSubModuleScope;
   aModule: TPasModule;
   aModule: TPasModule;
+  VarType: TPasType;
+  RecScope: TPasRecordScope;
+  SubScope: TPasSubRecordScope;
 begin
 begin
   //writeln('TPasResolver.ResolveSubIdent El.left=',GetObjName(El.left));
   //writeln('TPasResolver.ResolveSubIdent El.left=',GetObjName(El.left));
   if El.left.ClassType=TPrimitiveExpr then
   if El.left.ClassType=TPrimitiveExpr then
@@ -1512,13 +1585,38 @@ begin
         PushScope(ModuleScope);
         PushScope(ModuleScope);
         ResolveExpr(El.right);
         ResolveExpr(El.right);
         PopScope;
         PopScope;
+        exit;
+        end
+      else if DeclEl.ClassType=TPasVariable then
+        begin
+        VarType:=TPasVariable(DeclEl).VarType;
+        if VarType.ClassType=TPasRecordType then
+          begin
+          RecScope:=TPasRecordType(VarType).CustomData as TPasRecordScope;
+          SubScope:=TPasSubRecordScope.Create;
+          SubScope.Owner:=Self;
+          SubScope.RecordScope:=RecScope;
+          PushScope(SubScope);
+          ResolveExpr(El.right);
+          PopScope;
+          exit;
+          end
+        else
+          begin
+          {$IFDEF VerbosePasResolver}
+          writeln('TPasResolver.ResolveSubIdent DeclEl=',GetObjName(DeclEl),' VarType=',GetObjName(VarType));
+          {$ENDIF}
+          end;
+          end;
+        end
+      else
+        begin
+        {$IFDEF VerbosePasResolver}
+        writeln('TPasResolver.ResolveSubIdent DeclEl=',GetObjName(DeclEl));
+        {$ENDIF}
         end;
         end;
-      end
-    else
-      RaiseMsg(nIllegalQualifier,sIllegalQualifier,['.'],El);
-    end
-  else
-    RaiseMsg(nIllegalQualifier,sIllegalQualifier,['.'],El);
+    end;
+  RaiseMsg(nIllegalQualifier,sIllegalQualifier,['.'],El);
 end;
 end;
 
 
 procedure TPasResolver.ResolveParamsExpr(Params: TParamsExpr);
 procedure TPasResolver.ResolveParamsExpr(Params: TParamsExpr);
@@ -1558,7 +1656,7 @@ begin
     CreateReference(FindData.Found,Params.Value);
     CreateReference(FindData.Found,Params.Value);
     end
     end
   else
   else
-    RaiseNotYetImplemented(Params,' with parameters');
+    RaiseNotYetImplemented(Params,'with parameters');
 end;
 end;
 
 
 procedure TPasResolver.AddModule(El: TPasModule);
 procedure TPasResolver.AddModule(El: TPasModule);
@@ -1615,7 +1713,21 @@ begin
   {$ENDIF}
   {$ENDIF}
   if not (TopScope is TPasIdentifierScope) then
   if not (TopScope is TPasIdentifierScope) then
     RaiseInvalidScopeForElement(El);
     RaiseInvalidScopeForElement(El);
-  TPasIdentifierScope(TopScope).AddIdentifier(El.Name,El,pikSimple);
+  AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
+end;
+
+procedure TPasResolver.AddRecordType(El: TPasRecordType);
+begin
+  {$IFDEF VerbosePasResolver}
+  writeln('TPasResolver.AddRecordType ',GetObjName(El),' Parent=',GetObjName(El.Parent));
+  {$ENDIF}
+  if not (TopScope is TPasIdentifierScope) then
+    RaiseInvalidScopeForElement(El);
+  if El.Name<>'' then
+    AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
+
+  if El.Parent.ClassType<>TPasVariant then
+    PushScope(El,TPasRecordScope);
 end;
 end;
 
 
 procedure TPasResolver.AddVariable(El: TPasVariable);
 procedure TPasResolver.AddVariable(El: TPasVariable);
@@ -1626,7 +1738,7 @@ begin
   {$ENDIF}
   {$ENDIF}
   if not (TopScope is TPasIdentifierScope) then
   if not (TopScope is TPasIdentifierScope) then
     RaiseInvalidScopeForElement(El);
     RaiseInvalidScopeForElement(El);
-  TPasIdentifierScope(TopScope).AddIdentifier(El.Name,El,pikSimple);
+  AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikSimple);
 end;
 end;
 
 
 procedure TPasResolver.AddProcedure(El: TPasProcedure);
 procedure TPasResolver.AddProcedure(El: TPasProcedure);
@@ -1636,7 +1748,7 @@ begin
   {$ENDIF}
   {$ENDIF}
   if not (TopScope is TPasIdentifierScope) then
   if not (TopScope is TPasIdentifierScope) then
     RaiseInvalidScopeForElement(El);
     RaiseInvalidScopeForElement(El);
-  TPasIdentifierScope(TopScope).AddIdentifier(El.Name,El,pikProc);
+  AddIdentifier(TPasIdentifierScope(TopScope),El.Name,El,pikProc);
   PushScope(El,TPasProcedureScope);
   PushScope(El,TPasProcedureScope);
 end;
 end;
 
 
@@ -1649,14 +1761,14 @@ begin
   {$ENDIF}
   {$ENDIF}
   if not (TopScope is TPasProcedureScope) then
   if not (TopScope is TPasProcedureScope) then
     RaiseInvalidScopeForElement(El);
     RaiseInvalidScopeForElement(El);
-  TPasProcedureScope(TopScope).AddIdentifier(El.Name,El,pikSimple);
+  AddIdentifier(TPasProcedureScope(TopScope),El.Name,El,pikSimple);
 end;
 end;
 
 
 procedure TPasResolver.AddFunctionResult(El: TPasResultElement);
 procedure TPasResolver.AddFunctionResult(El: TPasResultElement);
 begin
 begin
   if TopScope.ClassType<>TPasProcedureScope then
   if TopScope.ClassType<>TPasProcedureScope then
     RaiseInvalidScopeForElement(El);
     RaiseInvalidScopeForElement(El);
-  TPasProcedureScope(TopScope).AddIdentifier(ResolverResultVar,El,pikSimple);
+  AddIdentifier(TPasProcedureScope(TopScope),ResolverResultVar,El,pikSimple);
 end;
 end;
 
 
 procedure TPasResolver.AddExceptOn(El: TPasImplExceptOn);
 procedure TPasResolver.AddExceptOn(El: TPasImplExceptOn);
@@ -1727,7 +1839,7 @@ begin
   writeln('TPasResolver.CreateElement ',AClass.ClassName,' Name=',AName,' Parent=',GetObjName(AParent),' (',ASrcPos.Row,',',ASrcPos.Column,')');
   writeln('TPasResolver.CreateElement ',AClass.ClassName,' Name=',AName,' Parent=',GetObjName(AParent),' (',ASrcPos.Row,',',ASrcPos.Column,')');
   {$ENDIF}
   {$ENDIF}
   if (AParent=nil) and (FRootElement<>nil)
   if (AParent=nil) and (FRootElement<>nil)
-  and (not AClass.InheritsFrom(TPasUnresolvedTypeRef)) then
+  and (AClass<>TPasUnresolvedTypeRef) then
     RaiseInternalError('TPasResolver.CreateElement more than one root element Class="'+AClass.ClassName+'" Root='+GetObjName(FRootElement));
     RaiseInternalError('TPasResolver.CreateElement more than one root element Class="'+AClass.ClassName+'" Root='+GetObjName(FRootElement));
 
 
   if ASrcPos.FileName='' then
   if ASrcPos.FileName='' then
@@ -1751,30 +1863,41 @@ begin
     FRootElement:=Result;
     FRootElement:=Result;
 
 
   // create scope
   // create scope
-  if AClass.InheritsFrom(TPasType) then
-    AddType(TPasType(El))
-  else if (AClass.ClassType=TPasVariable)
-      or (AClass.ClassType=TPasConst)
-      or (AClass.ClassType=TPasProperty) then
+  if (AClass=TPasVariable)
+      or (AClass=TPasConst)
+      or (AClass=TPasProperty) then
     AddVariable(TPasVariable(El))
     AddVariable(TPasVariable(El))
-  else if AClass.ClassType=TPasArgument then
+  else if AClass=TPasArgument then
     AddArgument(TPasArgument(El))
     AddArgument(TPasArgument(El))
+  else if AClass=TPasUnresolvedTypeRef then
+  else if (AClass=TPasAliasType)
+      or (AClass=TPasProcedureType)
+      or (AClass=TPasFunctionType) then
+    AddType(TPasType(El))
+  else if AClass=TPasRecordType then
+    AddRecordType(TPasRecordType(El))
+  else if AClass=TPasVariant then
   else if AClass.InheritsFrom(TPasProcedure) then
   else if AClass.InheritsFrom(TPasProcedure) then
     AddProcedure(TPasProcedure(El))
     AddProcedure(TPasProcedure(El))
-  else if AClass.ClassType=TPasResultElement then
+  else if AClass=TPasResultElement then
     AddFunctionResult(TPasResultElement(El))
     AddFunctionResult(TPasResultElement(El))
-  else if AClass.ClassType=TProcedureBody then
+  else if AClass=TProcedureBody then
     StartProcedureBody(TProcedureBody(El))
     StartProcedureBody(TProcedureBody(El))
-  else if AClass.InheritsFrom(TPasSection) then
+  else if AClass=TPasImplExceptOn then
+    AddExceptOn(TPasImplExceptOn(El))
+  else if AClass=TPasImplLabelMark then
+  else if AClass=TPasOverloadedProc then
+  else if (AClass=TInterfaceSection)
+      or (AClass=TImplementationSection)
+      or (AClass=TProgramSection)
+      or (AClass=TLibrarySection) then
     AddSection(TPasSection(El))
     AddSection(TPasSection(El))
-  else if AClass.InheritsFrom(TPasModule) then
+  else if (AClass=TPasModule)
+      or (AClass=TPasProgram)
+      or (AClass=TPasLibrary) then
     AddModule(TPasModule(El))
     AddModule(TPasModule(El))
   else if AClass.InheritsFrom(TPasExpr) then
   else if AClass.InheritsFrom(TPasExpr) then
-  else if AClass.ClassType=TPasImplExceptOn then
-    AddExceptOn(TPasImplExceptOn(El))
   else if AClass.InheritsFrom(TPasImplBlock) then
   else if AClass.InheritsFrom(TPasImplBlock) then
-  else if AClass.ClassType=TPasImplLabelMark then
-  else if AClass.ClassType=TPasOverloadedProc then
   else
   else
     RaiseNotYetImplemented(El);
     RaiseNotYetImplemented(El);
 end;
 end;
@@ -1818,13 +1941,13 @@ begin
     end;
     end;
 end;
 end;
 
 
-procedure TPasResolver.FinishScope(ScopeType: TPasScopeType);
+procedure TPasResolver.FinishScope(ScopeType: TPasScopeType; El: TPasElement);
 begin
 begin
   case ScopeType of
   case ScopeType of
-  stModule: FinishModule;
+  stModule: FinishModule(El as TPasModule);
   stUsesList: FinishUsesList;
   stUsesList: FinishUsesList;
   stTypeSection: FinishTypeSection;
   stTypeSection: FinishTypeSection;
-  stTypeDef: ;
+  stTypeDef: FinishTypeDef(El as TPasType);
   stProcedure: FinishProcedure;
   stProcedure: FinishProcedure;
   stProcedureHeader: FinishProcedureHeader;
   stProcedureHeader: FinishProcedureHeader;
   stExceptOnExpr: FinishExceptOnExpr;
   stExceptOnExpr: FinishExceptOnExpr;
@@ -1846,6 +1969,18 @@ begin
   end;
   end;
 end;
 end;
 
 
+class function TPasResolver.GetElementSourcePosStr(El: TPasElement): string;
+var
+  Line, Column: integer;
+begin
+  if El=nil then exit('nil');
+  UnmangleSourceLineNumber(El.SourceLinenumber,Line,Column);
+  Result:=El.SourceFilename+'('+IntToStr(Line);
+  if Column>0 then
+    Result:=Result+','+IntToStr(Column);
+  Result:=Result+')';
+end;
+
 destructor TPasResolver.Destroy;
 destructor TPasResolver.Destroy;
 begin
 begin
   Clear;
   Clear;
@@ -1876,7 +2011,7 @@ var
   bt: TResolveBaseType;
   bt: TResolveBaseType;
 begin
 begin
   for bt in BaseTypes do
   for bt in BaseTypes do
-    FDefaultScope.AddIdentifier(BaseTypeNames[bt],
+    AddIdentifier(FDefaultScope,BaseTypeNames[bt],
       TPasUnresolvedSymbolRef.Create(BaseTypeNames[bt],nil),pikCustom);
       TPasUnresolvedSymbolRef.Create(BaseTypeNames[bt],nil),pikCustom);
 end;
 end;
 
 
@@ -1885,12 +2020,10 @@ function TPasResolver.CreateReference(DeclEl, RefEl: TPasElement
 
 
   procedure RaiseAlreadySet;
   procedure RaiseAlreadySet;
   var
   var
-    aLine, aCol: integer;
     FormerDeclEl: TPasElement;
     FormerDeclEl: TPasElement;
   begin
   begin
     writeln('RaiseAlreadySet RefEl=',GetObjName(RefEl),' DeclEl=',GetObjName(DeclEl));
     writeln('RaiseAlreadySet RefEl=',GetObjName(RefEl),' DeclEl=',GetObjName(DeclEl));
-    UnmangleSourceLineNumber(RefEl.SourceLinenumber,aLine,aCol);
-    writeln('  RefEl at ',RefEl.SourceFilename,'(',aLine,',',aCol,')');
+    writeln('  RefEl at ',GetElementSourcePosStr(RefEl));
     writeln('  RefEl.CustomData=',GetObjName(RefEl.CustomData));
     writeln('  RefEl.CustomData=',GetObjName(RefEl.CustomData));
     if RefEl.CustomData is TResolvedReference then
     if RefEl.CustomData is TResolvedReference then
       begin
       begin
@@ -1969,7 +2102,9 @@ begin
   FScopes[FScopeCount]:=Scope;
   FScopes[FScopeCount]:=Scope;
   inc(FScopeCount);
   inc(FScopeCount);
   FTopScope:=Scope;
   FTopScope:=Scope;
+  {$IFDEF VerbosePasResolver}
   writeln('TPasResolver.PushScope ScopeCount=',ScopeCount,' ',GetObjName(FTopScope),' IsDefault=',FDefaultScope=FTopScope);
   writeln('TPasResolver.PushScope ScopeCount=',ScopeCount,' ',GetObjName(FTopScope),' IsDefault=',FDefaultScope=FTopScope);
+  {$ENDIF}
 end;
 end;
 
 
 procedure TPasResolver.SetLastMsg(MsgType: TMessageType; MsgNumber: integer;
 procedure TPasResolver.SetLastMsg(MsgType: TMessageType; MsgNumber: integer;
@@ -1997,8 +2132,13 @@ begin
 end;
 end;
 
 
 procedure TPasResolver.RaiseNotYetImplemented(El: TPasElement; Msg: string);
 procedure TPasResolver.RaiseNotYetImplemented(El: TPasElement; Msg: string);
+var
+  s: String;
 begin
 begin
-  RaiseMsg(nNotYetImplemented,sNotYetImplemented+Msg,[GetObjName(El)],El);
+  s:=sNotYetImplemented;
+  if Msg<>'' then
+    s:=s+Msg;
+  RaiseMsg(nNotYetImplemented,s,[GetObjName(El)],El);
 end;
 end;
 
 
 procedure TPasResolver.RaiseInternalError(const Msg: string);
 procedure TPasResolver.RaiseInternalError(const Msg: string);

+ 9 - 10
packages/fcl-passrc/src/pastree.pp

@@ -558,8 +558,7 @@ type
   public
   public
     PackMode: TPackMode;
     PackMode: TPackMode;
     Members: TFPList;     // array of TPasVariable elements
     Members: TFPList;     // array of TPasVariable elements
-    VariantName: string;
-    VariantType: TPasType;
+    VariantEl: TPasElement; // TPasVariable or TPasType
     Variants: TFPList;	// array of TPasVariant elements, may be nil!
     Variants: TFPList;	// array of TPasVariant elements, may be nil!
     Function IsPacked: Boolean;
     Function IsPacked: Boolean;
     Function IsBitPacked : Boolean;
     Function IsBitPacked : Boolean;
@@ -2173,8 +2172,8 @@ begin
     TPasVariable(Members[i]).Release;
     TPasVariable(Members[i]).Release;
   Members.Free;
   Members.Free;
 
 
-  if Assigned(VariantType) then
-    VariantType.Release;
+  if Assigned(VariantEl) then
+    VariantEl.Release;
 
 
   if Assigned(Variants) then
   if Assigned(Variants) then
   begin
   begin
@@ -3125,10 +3124,10 @@ begin
   if Variants<>nil then
   if Variants<>nil then
     begin
     begin
     temp:='case ';
     temp:='case ';
-    if (VariantName<>'') then
-      temp:=Temp+variantName+' : ';
-    if (VariantType<>Nil) then
-      temp:=temp+VariantType.Name;
+    if (VariantEl is TPasVariable) then
+      temp:=Temp+VariantEl.Name+' : '+TPasVariable(VariantEl).VarType.Name
+    else if (VariantEl<>Nil) then
+      temp:=temp+VariantEl.Name;
     S.Add(temp+' of');
     S.Add(temp+' of');
     T.Clear;
     T.Clear;
     For I:=0 to Variants.Count-1 do
     For I:=0 to Variants.Count-1 do
@@ -3175,8 +3174,8 @@ begin
   inherited ForEachCall(aMethodCall, Arg);
   inherited ForEachCall(aMethodCall, Arg);
   for i:=0 to Members.Count-1 do
   for i:=0 to Members.Count-1 do
     TPasElement(Members[i]).ForEachCall(aMethodCall,Arg);
     TPasElement(Members[i]).ForEachCall(aMethodCall,Arg);
-  if VariantType<>nil then
-    VariantType.ForEachCall(aMethodCall,Arg);
+  if VariantEl<>nil then
+    VariantEl.ForEachCall(aMethodCall,Arg);
   if Variants<>nil then
   if Variants<>nil then
     for i:=0 to Variants.Count-1 do
     for i:=0 to Variants.Count-1 do
       TPasElement(Variants[i]).ForEachCall(aMethodCall,Arg);
       TPasElement(Variants[i]).ForEachCall(aMethodCall,Arg);

+ 75 - 31
packages/fcl-passrc/src/pparser.pp

@@ -176,7 +176,7 @@ type
     function CreateFunctionType(const AName, AResultName: String; AParent: TPasElement;
     function CreateFunctionType(const AName, AResultName: String; AParent: TPasElement;
       UseParentAsResultParent: Boolean; const ASrcPos: TPasSourcePos): TPasFunctionType;
       UseParentAsResultParent: Boolean; const ASrcPos: TPasSourcePos): TPasFunctionType;
     function FindElement(const AName: String): TPasElement; virtual; abstract;
     function FindElement(const AName: String): TPasElement; virtual; abstract;
-    procedure FinishScope(ScopeType: TPasScopeType); virtual;
+    procedure FinishScope(ScopeType: TPasScopeType; El: TPasElement); virtual;
     function FindModule(const AName: String): TPasModule; virtual;
     function FindModule(const AName: String): TPasModule; virtual;
     property Package: TPasPackage read FPackage;
     property Package: TPasPackage read FPackage;
     property InterfaceOnly : Boolean Read FInterfaceOnly Write FInterFaceOnly;
     property InterfaceOnly : Boolean Read FInterfaceOnly Write FInterFaceOnly;
@@ -239,7 +239,6 @@ type
     function GetVariableModifiers(Out VarMods : TVariableModifiers; Out Libname,ExportName : string): string;
     function GetVariableModifiers(Out VarMods : TVariableModifiers; Out Libname,ExportName : string): string;
     function GetVariableValueAndLocation(Parent : TPasElement; Out Value : TPasExpr; Out Location: String): Boolean;
     function GetVariableValueAndLocation(Parent : TPasElement; Out Value : TPasExpr; Out Location: String): Boolean;
     procedure HandleProcedureModifier(Parent: TPasElement; pm : TProcedureModifier);
     procedure HandleProcedureModifier(Parent: TPasElement; pm : TProcedureModifier);
-    procedure ParseAsmBlock(AsmBlock: TPasImplAsmStatement);
     procedure ParseClassLocalConsts(AType: TPasClassType; AVisibility: TPasMemberVisibility);
     procedure ParseClassLocalConsts(AType: TPasClassType; AVisibility: TPasMemberVisibility);
     procedure ParseClassLocalTypes(AType: TPasClassType; AVisibility: TPasMemberVisibility);
     procedure ParseClassLocalTypes(AType: TPasClassType; AVisibility: TPasMemberVisibility);
     procedure ParseVarList(Parent: TPasElement; VarList: TFPList; AVisibility: TPasMemberVisibility; Full: Boolean);
     procedure ParseVarList(Parent: TPasElement; VarList: TFPList; AVisibility: TPasMemberVisibility; Full: Boolean);
@@ -251,6 +250,7 @@ type
     Procedure DoLog(MsgType: TMessageType; MsgNumber: integer; Const Msg : String; 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;
     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 ParseAsmBlock(AsmBlock: TPasImplAsmStatement); virtual;
     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);
     function GetProcedureClass(ProcType : TProcType): TPTreeElement;
     function GetProcedureClass(ProcType : TProcType): TPTreeElement;
@@ -658,9 +658,11 @@ begin
     visDefault, ASrcPos));
     visDefault, ASrcPos));
 end;
 end;
 
 
-procedure TPasTreeContainer.FinishScope(ScopeType: TPasScopeType);
+procedure TPasTreeContainer.FinishScope(ScopeType: TPasScopeType;
+  El: TPasElement);
 begin
 begin
   if ScopeType=stModule then ;
   if ScopeType=stModule then ;
+  if El=nil then ;
 end;
 end;
 
 
 function TPasTreeContainer.FindModule(const AName: String): TPasModule;
 function TPasTreeContainer.FindModule(const AName: String): TPasModule;
@@ -901,7 +903,7 @@ begin
   if result and (pm in [pmPublic,pmForward]) then
   if result and (pm in [pmPublic,pmForward]) then
     begin
     begin
     While (Parent<>Nil) and Not ((Parent is TPasClassType) or (Parent is TPasRecordType)) do
     While (Parent<>Nil) and Not ((Parent is TPasClassType) or (Parent is TPasRecordType)) do
-     Parent:=Parent.Parent;
+      Parent:=Parent.Parent;
     Result:=Not Assigned(Parent);
     Result:=Not Assigned(Parent);
     end;
     end;
 end;
 end;
@@ -1826,7 +1828,7 @@ begin
       tkColon: // record field (a:xxx;b:yyy;c:zzz);
       tkColon: // record field (a:xxx;b:yyy;c:zzz);
         begin
         begin
           n:=GetExprIdent(x);
           n:=GetExprIdent(x);
-          x.Free;
+          x.Release;
           r:=CreateRecordValues(AParent);
           r:=CreateRecordValues(AParent);
           NextToken;
           NextToken;
           x:=DoParseConstValueExpression(AParent);
           x:=DoParseConstValueExpression(AParent);
@@ -1850,7 +1852,8 @@ begin
         Result:=DoParseExpression(AParent,Result);
         Result:=DoParseExpression(AParent,Result);
       Exit;
       Exit;
     end;
     end;
-    if CurToken<>tkBraceClose then ParseExc(nParserExpectedCommaRBracket,SParserExpectedCommaRBracket);
+    if CurToken<>tkBraceClose then
+      ParseExc(nParserExpectedCommaRBracket,SParserExpectedCommaRBracket);
     NextToken;
     NextToken;
   end;
   end;
 end;
 end;
@@ -1984,7 +1987,7 @@ begin
     If LogEvent(pleInterface) then
     If LogEvent(pleInterface) then
       DoLog(mtInfo,nLogStartInterface,SLogStartInterface);
       DoLog(mtInfo,nLogStartInterface,SLogStartInterface);
     ParseInterface;
     ParseInterface;
-    Engine.FinishScope(stModule);
+    Engine.FinishScope(stModule,Module);
   finally
   finally
     FCurModule:=nil;
     FCurModule:=nil;
   end;
   end;
@@ -2034,7 +2037,7 @@ begin
     PP.ProgramSection := Section;
     PP.ProgramSection := Section;
     ParseOptionalUsesList(Section);
     ParseOptionalUsesList(Section);
     ParseDeclarations(Section);
     ParseDeclarations(Section);
-    Engine.FinishScope(stModule);
+    Engine.FinishScope(stModule,Module);
   finally
   finally
     FCurModule:=nil;
     FCurModule:=nil;
   end;
   end;
@@ -2063,7 +2066,7 @@ begin
     PP.LibrarySection := Section;
     PP.LibrarySection := Section;
     ParseOptionalUsesList(Section);
     ParseOptionalUsesList(Section);
     ParseDeclarations(Section);
     ParseDeclarations(Section);
-    Engine.FinishScope(stModule);
+    Engine.FinishScope(stModule,Module);
   finally
   finally
     FCurModule:=nil;
     FCurModule:=nil;
   end;
   end;
@@ -2077,7 +2080,7 @@ begin
     ParseUsesList(ASection)
     ParseUsesList(ASection)
   else begin
   else begin
     CheckImplicitUsedUnits(ASection);
     CheckImplicitUsedUnits(ASection);
-    Engine.FinishScope(stUsesList);
+    Engine.FinishScope(stUsesList,ASection);
     UngetToken;
     UngetToken;
   end;
   end;
 end;
 end;
@@ -2201,7 +2204,7 @@ var
   begin
   begin
     if CurBlock=NewBlock then exit;
     if CurBlock=NewBlock then exit;
     if CurBlock=declType then
     if CurBlock=declType then
-      Engine.FinishScope(stTypeDef);
+      Engine.FinishScope(stTypeSection,Declarations);
     CurBlock:=NewBlock;
     CurBlock:=NewBlock;
   end;
   end;
 
 
@@ -2540,7 +2543,7 @@ begin
       ParseExc(nParserExpectedCommaSemicolon,SParserExpectedCommaSemicolon);
       ParseExc(nParserExpectedCommaSemicolon,SParserExpectedCommaSemicolon);
   Until (CurToken=tkSemicolon);
   Until (CurToken=tkSemicolon);
 
 
-  Engine.FinishScope(stUsesList);
+  Engine.FinishScope(stUsesList,ASection);
 end;
 end;
 
 
 // Starts after the variable name
 // Starts after the variable name
@@ -2823,6 +2826,7 @@ begin
   ok:=false;
   ok:=false;
   try
   try
     D:=SaveComments; // This means we support only one comment per 'list'.
     D:=SaveComments; // This means we support only one comment per 'list'.
+    VarEl:=nil;
     Repeat
     Repeat
       // create the TPasVariable here, so that SourceLineNumber is correct
       // create the TPasVariable here, so that SourceLineNumber is correct
       VarEl:=TPasVariable(CreateElement(TPasVariable,CurTokenString,Parent,AVisibility));
       VarEl:=TPasVariable(CreateElement(TPasVariable,CurTokenString,Parent,AVisibility));
@@ -2835,13 +2839,13 @@ begin
     Until (CurToken=tkColon);
     Until (CurToken=tkColon);
 
 
     // read type
     // read type
-    VarType := ParseComplexType(Parent);
+    VarType := ParseComplexType(VarEl);
     for i := OldListCount to VarList.Count - 1 do
     for i := OldListCount to VarList.Count - 1 do
       begin
       begin
       VarEl:=TPasVariable(VarList[i]);
       VarEl:=TPasVariable(VarList[i]);
       // Writeln(VarEl.Name, AVisibility);
       // Writeln(VarEl.Name, AVisibility);
       VarEl.VarType := VarType;
       VarEl.VarType := VarType;
-      VarType.Parent := VarEl;
+      //VarType.Parent := VarEl; // this is wrong for references types
       if (i>=OldListCount) then
       if (i>=OldListCount) then
         VarType.AddRef;
         VarType.AddRef;
       end;
       end;
@@ -3231,6 +3235,7 @@ Var
   CC : TCallingConvention;
   CC : TCallingConvention;
   PM : TProcedureModifier;
   PM : TProcedureModifier;
   Done: Boolean;
   Done: Boolean;
+  ResultEl: TPasResultElement;
 
 
 begin
 begin
   // Element must be non-nil. Removed all checks for not-nil.
   // Element must be non-nil. Removed all checks for not-nil.
@@ -3240,22 +3245,24 @@ begin
     ptFunction,ptClassFunction:
     ptFunction,ptClassFunction:
       begin
       begin
       ExpectToken(tkColon);
       ExpectToken(tkColon);
-      TPasFunctionType(Element).ResultEl.ResultType := ParseType(Parent,Scanner.CurSourcePos);
+      ResultEl:=TPasFunctionType(Element).ResultEl;
+      ResultEl.ResultType := ParseType(ResultEl,Scanner.CurSourcePos);
       end;
       end;
     ptOperator,ptClassOperator:
     ptOperator,ptClassOperator:
       begin
       begin
       NextToken;
       NextToken;
+      ResultEl:=TPasFunctionType(Element).ResultEl;
       if (CurToken=tkIdentifier) then
       if (CurToken=tkIdentifier) then
         begin
         begin
-        TPasFunctionType(Element).ResultEl.Name := CurTokenName;
+        ResultEl.Name := CurTokenName;
         ExpectToken(tkColon);
         ExpectToken(tkColon);
         end
         end
       else
       else
         if (CurToken=tkColon) then
         if (CurToken=tkColon) then
-          TPasFunctionType(Element).ResultEl.Name := 'Result'
+          ResultEl.Name := 'Result'
         else
         else
           ParseExc(nParserExpectedColonID,SParserExpectedColonID);
           ParseExc(nParserExpectedColonID,SParserExpectedColonID);
-        TPasFunctionType(Element).ResultEl.ResultType := ParseType(Parent,Scanner.CurSourcePos)
+        ResultEl.ResultType := ParseType(ResultEl,Scanner.CurSourcePos)
       end;
       end;
   end;
   end;
   if OfObjectPossible then
   if OfObjectPossible then
@@ -3343,7 +3350,7 @@ begin
     ConsumeSemi;
     ConsumeSemi;
   if (ProcType in [ptOperator,ptClassOperator]) and (Parent is TPasOperator) then
   if (ProcType in [ptOperator,ptClassOperator]) and (Parent is TPasOperator) then
     TPasOperator(Parent).CorrectName;
     TPasOperator(Parent).CorrectName;
-  Engine.FinishScope(stProcedureHeader);
+  Engine.FinishScope(stProcedureHeader,Element);
   if (Parent is TPasProcedure)
   if (Parent is TPasProcedure)
   and (not TPasProcedure(Parent).IsForward)
   and (not TPasProcedure(Parent).IsForward)
   and (not TPasProcedure(Parent).IsExternal)
   and (not TPasProcedure(Parent).IsExternal)
@@ -3351,7 +3358,7 @@ begin
      or (Parent.Parent is TProcedureBody))
      or (Parent.Parent is TProcedureBody))
   then
   then
     ParseProcedureBody(Parent);
     ParseProcedureBody(Parent);
-  Engine.FinishScope(stProcedure);
+  Engine.FinishScope(stProcedure,Parent);
 end;
 end;
 
 
 // starts after the semicolon
 // starts after the semicolon
@@ -3527,13 +3534,45 @@ begin
 end;
 end;
 
 
 procedure TPasParser.ParseAsmBlock(AsmBlock : TPasImplAsmStatement);
 procedure TPasParser.ParseAsmBlock(AsmBlock : TPasImplAsmStatement);
-
 begin
 begin
-  NextToken;
-  While CurToken<>tkEnd do
+  if po_asmwhole in Options then
+    begin
+    FTokenBufferIndex:=1;
+    FTokenBufferSize:=1;
+    FCommentsBuffer[0].Clear;
+    repeat
+      Scanner.ReadNonPascalTilEndToken(true);
+      case Scanner.CurToken of
+      tkLineEnding:
+        AsmBlock.Tokens.Add(Scanner.CurTokenString);
+      tkend:
+        begin
+        FTokenBuffer[0] := tkend;
+        FTokenStringBuffer[0] := Scanner.CurTokenString;
+        break;
+        end
+      else
+        begin
+        // missing end
+        FTokenBuffer[0] := tkEOF;
+        FTokenStringBuffer[0] := '';
+        end;
+      end;
+    until false;
+    FCurToken := FTokenBuffer[0];
+    FCurTokenString := FTokenStringBuffer[0];
+    FCurComments:=FCommentsBuffer[0];
+    CheckToken(tkend);
+    end
+  else
     begin
     begin
-    AsmBlock.Tokens.Add(CurTokenText);
     NextToken;
     NextToken;
+    While CurToken<>tkEnd do
+      begin
+      // ToDo: allow @@end
+      AsmBlock.Tokens.Add(CurTokenText);
+      NextToken;
+      end;
     end;
     end;
   // NextToken; // Eat end.
   // NextToken; // Eat end.
   // Do not consume end. Current token will normally be end;
   // Do not consume end. Current token will normally be end;
@@ -3563,7 +3602,7 @@ var
   function CloseBlock: boolean; // true if parent reached
   function CloseBlock: boolean; // true if parent reached
   begin
   begin
     if CurBlock.ClassType=TPasImplExceptOn then
     if CurBlock.ClassType=TPasImplExceptOn then
-      Engine.FinishScope(stExceptOnStatement);
+      Engine.FinishScope(stExceptOnStatement,CurBlock);
     CurBlock:=CurBlock.Parent as TPasImplBlock;
     CurBlock:=CurBlock.Parent as TPasImplBlock;
     Result:=CurBlock=Parent;
     Result:=CurBlock=Parent;
   end;
   end;
@@ -3897,7 +3936,7 @@ begin
           El:=TPasImplExceptOn(CreateElement(TPasImplExceptOn,'',CurBlock));
           El:=TPasImplExceptOn(CreateElement(TPasImplExceptOn,'',CurBlock));
           TPasImplExceptOn(El).VarExpr:=Left;
           TPasImplExceptOn(El).VarExpr:=Left;
           TPasImplExceptOn(El).TypeExpr:=Right;
           TPasImplExceptOn(El).TypeExpr:=Right;
-          Engine.FinishScope(stExceptOnExpr);
+          Engine.FinishScope(stExceptOnExpr,El);
           CurBlock.AddElement(El);
           CurBlock.AddElement(El);
           CurBlock:=TPasImplExceptOn(El);
           CurBlock:=TPasImplExceptOn(El);
           ExpectToken(tkDo);
           ExpectToken(tkDo);
@@ -4184,14 +4223,14 @@ procedure TPasParser.ParseRecordFieldList(ARec: TPasRecordType;
   AEndToken: TToken; AllowMethods: Boolean);
   AEndToken: TToken; AllowMethods: Boolean);
 
 
 Var
 Var
-  VN : String;
+  VariantName : String;
   v : TPasmemberVisibility;
   v : TPasmemberVisibility;
   Proc: TPasProcedure;
   Proc: TPasProcedure;
   ProcType: TProcType;
   ProcType: TProcType;
   Prop : TPasProperty;
   Prop : TPasProperty;
   Cons : TPasConst;
   Cons : TPasConst;
   isClass : Boolean;
   isClass : Boolean;
-
+  NamePos: TPasSourcePos;
 begin
 begin
   v:=visDefault;
   v:=visDefault;
   isClass:=False;
   isClass:=False;
@@ -4256,16 +4295,20 @@ begin
         begin
         begin
         ARec.Variants:=TFPList.Create;
         ARec.Variants:=TFPList.Create;
         NextToken;
         NextToken;
-        VN:=CurTokenString;
+        VariantName:=CurTokenString;
+        NamePos:=Scanner.CurSourcePos;
         NextToken;
         NextToken;
         If CurToken=tkColon then
         If CurToken=tkColon then
-          ARec.VariantName:=VN
+          begin
+          ARec.VariantEl:=TPasVariable(CreateElement(TPasVariable,VariantName,ARec,NamePos));
+          TPasVariable(ARec.VariantEl).VarType:=ParseType(ARec,Scanner.CurSourcePos);
+          end
         else
         else
           begin
           begin
           UnGetToken;
           UnGetToken;
           UnGetToken;
           UnGetToken;
+          ARec.VariantEl:=ParseType(ARec,Scanner.CurSourcePos);
           end;
           end;
-        ARec.VariantType:=ParseType(ARec,Scanner.CurSourcePos);
         ExpectToken(tkOf);
         ExpectToken(tkOf);
         ParseRecordVariantParts(ARec,AEndToken);
         ParseRecordVariantParts(ARec,AEndToken);
         end;
         end;
@@ -4293,6 +4336,7 @@ begin
     Result.PackMode:=PackMode;
     Result.PackMode:=PackMode;
     NextToken;
     NextToken;
     ParseRecordFieldList(Result,tkEnd,true);
     ParseRecordFieldList(Result,tkEnd,true);
+    Engine.FinishScope(stTypeDef,Result);
     ok:=true;
     ok:=true;
   finally
   finally
     if not ok then
     if not ok then

+ 100 - 20
packages/fcl-passrc/src/pscanner.pp

@@ -328,7 +328,8 @@ type
   TPOption = (
   TPOption = (
     po_delphi, // Delphi mode: forbid nested comments
     po_delphi, // Delphi mode: forbid nested comments
     po_cassignments,  // allow C-operators += -= *= /=
     po_cassignments,  // allow C-operators += -= *= /=
-    po_resolvestandardtypes // search for 'longint', 'string', etc., do not use dummies, TPasResolver sets this to use its declarations
+    po_resolvestandardtypes, // search for 'longint', 'string', etc., do not use dummies, TPasResolver sets this to use its declarations
+    po_asmwhole  // store whole text between asm..end in TPasImplAsmStatement.Tokens
     );
     );
   TPOptions = set of TPOption;
   TPOptions = set of TPOption;
 
 
@@ -379,6 +380,7 @@ type
     function GetCurColumn: Integer;
     function GetCurColumn: Integer;
     procedure SetOptions(AValue: TPOptions);
     procedure SetOptions(AValue: TPOptions);
   protected
   protected
+    function FetchLine: boolean;
     procedure SetCurMsg(MsgType: TMessageType; MsgNumber: integer; Const Fmt : String; Args : Array of const);
     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 Msg : String; SkipSourceInfo : Boolean = False);overload;
     Procedure DoLog(MsgType: TMessageType; MsgNumber: integer; Const Fmt : String; Args : Array of const;SkipSourceInfo : Boolean = False);overload;
     Procedure DoLog(MsgType: TMessageType; MsgNumber: integer; Const Fmt : String; Args : Array of const;SkipSourceInfo : Boolean = False);overload;
@@ -400,6 +402,7 @@ type
     destructor Destroy; override;
     destructor Destroy; override;
     procedure OpenFile(const AFilename: string);
     procedure OpenFile(const AFilename: string);
     function FetchToken: TToken;
     function FetchToken: TToken;
+    function ReadNonPascalTilEndToken(StopAtLineEnd: boolean): TToken;
     Procedure AddDefine(S : String);
     Procedure AddDefine(S : String);
     Procedure RemoveDefine(S : String);
     Procedure RemoveDefine(S : String);
     function CurSourcePos: TPasSourcePos;
     function CurSourcePos: TPasSourcePos;
@@ -1159,6 +1162,84 @@ begin
 //  Writeln(Result, '(',CurTokenString,')');
 //  Writeln(Result, '(',CurTokenString,')');
 end;
 end;
 
 
+function TPascalScanner.ReadNonPascalTilEndToken(StopAtLineEnd: boolean
+  ): TToken;
+var
+  StartPos: PChar;
+
+  Procedure Add;
+  var
+    AddLen: PtrInt;
+    OldLen: Integer;
+  begin
+    AddLen:=TokenStr-StartPos;
+    if AddLen=0 then exit;
+    OldLen:=length(FCurTokenString);
+    SetLength(FCurTokenString,OldLen+AddLen);
+    Move(StartPos^,PChar(PChar(FCurTokenString)+OldLen)^,AddLen);
+    StartPos:=TokenStr;
+  end;
+
+begin
+  FCurTokenString := '';
+  if (TokenStr = nil) or (TokenStr^ = #0) then
+    if not FetchLine then
+    begin
+      Result := tkEOF;
+      FCurToken := Result;
+      exit;
+    end;
+
+  StartPos:=TokenStr;
+  repeat
+    case TokenStr[0] of
+      #0: // end of line
+        begin
+          Add;
+          if StopAtLineEnd then
+            begin
+            Result := tkLineEnding;
+            FCurToken := Result;
+            exit;
+            end;
+          if not FetchLine then
+            begin
+            Result := tkEOF;
+            FCurToken := Result;
+            exit;
+            end;
+          StartPos:=TokenStr;
+        end;
+      '0'..'9', 'A'..'Z', 'a'..'z','_':
+        begin
+          // number or identifier
+          if (TokenStr[0] in ['e','E'])
+              and (TokenStr[1] in ['n','N'])
+              and (TokenStr[2] in ['d','D'])
+              and not (TokenStr[3] in ['0'..'9', 'A'..'Z', 'a'..'z','_']) then
+            begin
+            // 'end' found
+            Add;
+            Result := tkend;
+            SetLength(FCurTokenString, 3);
+            Move(TokenStr^, FCurTokenString[1], 3);
+            inc(TokenStr,3);
+            FCurToken := Result;
+            exit;
+            end
+          else
+            begin
+            // skip identifier
+            while TokenStr[0] in ['0'..'9', 'A'..'Z', 'a'..'z','_'] do
+              inc(TokenStr);
+            end;
+        end;
+      else
+        inc(TokenStr);
+    end;
+  until false;
+end;
+
 procedure TPascalScanner.Error(MsgNumber: integer; const Msg: string);
 procedure TPascalScanner.Error(MsgNumber: integer; const Msg: string);
 begin
 begin
   SetCurMsg(mtError,MsgNumber,Msg,[]);
   SetCurMsg(mtError,MsgNumber,Msg,[]);
@@ -1335,25 +1416,6 @@ begin
 end;
 end;
 
 
 function TPascalScanner.DoFetchToken: TToken;
 function TPascalScanner.DoFetchToken: TToken;
-
-  function FetchLine: Boolean;
-  begin
-    if CurSourceFile.IsEOF then
-    begin
-      FCurLine := '';
-      TokenStr := nil;
-      Result := false;
-    end else
-    begin
-      FCurLine := CurSourceFile.ReadLine;
-      TokenStr := PChar(CurLine);
-      Result := true;
-      Inc(FCurRow);
-      if LogEvent(sleLineNumber) and ((FCurRow Mod 100) = 0) then
-        DoLog(mtInfo,nLogLineNumber,SLogLineNumber,[FCurRow],True);
-    end;
-  end;
-
 var
 var
   TokenStart, CurPos: PChar;
   TokenStart, CurPos: PChar;
   i: TToken;
   i: TToken;
@@ -1935,6 +1997,24 @@ begin
   FOptions:=AValue;
   FOptions:=AValue;
 end;
 end;
 
 
+function TPascalScanner.FetchLine: boolean;
+begin
+  if CurSourceFile.IsEOF then
+  begin
+    FCurLine := '';
+    TokenStr := nil;
+    Result := false;
+  end else
+  begin
+    FCurLine := CurSourceFile.ReadLine;
+    TokenStr := PChar(CurLine);
+    Result := true;
+    Inc(FCurRow);
+    if LogEvent(sleLineNumber) and ((FCurRow Mod 100) = 0) then
+      DoLog(mtInfo,nLogLineNumber,SLogLineNumber,[FCurRow],True);
+  end;
+end;
+
 procedure TPascalScanner.SetCurMsg(MsgType: TMessageType; MsgNumber: integer;
 procedure TPascalScanner.SetCurMsg(MsgType: TMessageType; MsgNumber: integer;
   const Fmt: String; Args: array of const);
   const Fmt: String; Args: array of const);
 begin
 begin

+ 97 - 12
packages/fcl-passrc/tests/tcresolver.pas

@@ -64,7 +64,7 @@ Type
   Private
   Private
     FFirstStatement: TPasImplBlock;
     FFirstStatement: TPasImplBlock;
     FModules: TObjectList;// list of TTestEnginePasResolver
     FModules: TObjectList;// list of TTestEnginePasResolver
-    FPasResolver: TTestEnginePasResolver;
+    FResolverEngine: TTestEnginePasResolver;
     function GetModuleCount: integer;
     function GetModuleCount: integer;
     function GetModules(Index: integer): TTestEnginePasResolver;
     function GetModules(Index: integer): TTestEnginePasResolver;
     function OnPasResolverFindUnit(const aUnitName: String): TPasModule;
     function OnPasResolverFindUnit(const aUnitName: String): TPasModule;
@@ -109,7 +109,11 @@ Type
     Procedure TestProcOverload;
     Procedure TestProcOverload;
     Procedure TestProcOverloadRefs;
     Procedure TestProcOverloadRefs;
     Procedure TestNestedProc;
     Procedure TestNestedProc;
-    property PasResolver: TTestEnginePasResolver read FPasResolver;
+    Procedure TestDuplicateVar;
+    Procedure TestRecord;
+    Procedure TestRecordVariant;
+    Procedure TestRecordVariantNested;
+    property ResolverEngine: TTestEnginePasResolver read FResolverEngine;
   end;
   end;
 
 
 function LinesToStr(Args: array of const): string;
 function LinesToStr(Args: array of const): string;
@@ -182,22 +186,22 @@ end;
 
 
 procedure TTestResolver.TearDown;
 procedure TTestResolver.TearDown;
 begin
 begin
-  PasResolver.Clear;
+  ResolverEngine.Clear;
   if FModules<>nil then
   if FModules<>nil then
     begin
     begin
     FModules.OwnsObjects:=false;
     FModules.OwnsObjects:=false;
-    FModules.Remove(PasResolver); // remove reference
+    FModules.Remove(ResolverEngine); // remove reference
     FModules.OwnsObjects:=true;
     FModules.OwnsObjects:=true;
     FreeAndNil(FModules);// free all other modules
     FreeAndNil(FModules);// free all other modules
     end;
     end;
   inherited TearDown;
   inherited TearDown;
-  FPasResolver:=nil;
+  FResolverEngine:=nil;
 end;
 end;
 
 
 procedure TTestResolver.CreateEngine(var TheEngine: TPasTreeContainer);
 procedure TTestResolver.CreateEngine(var TheEngine: TPasTreeContainer);
 begin
 begin
-  FPasResolver:=AddModule(MainFilename);
-  TheEngine:=PasResolver;
+  FResolverEngine:=AddModule(MainFilename);
+  TheEngine:=ResolverEngine;
 end;
 end;
 
 
 procedure TTestResolver.ParseProgram;
 procedure TTestResolver.ParseProgram;
@@ -232,7 +236,7 @@ begin
       raise E;
       raise E;
       end;
       end;
   end;
   end;
-  TAssert.AssertSame('Has resolver',PasResolver,Parser.Engine);
+  TAssert.AssertSame('Has resolver',ResolverEngine,Parser.Engine);
   AssertEquals('Has program',TPasProgram,Module.ClassType);
   AssertEquals('Has program',TPasProgram,Module.ClassType);
   AssertNotNull('Has program section',PasProgram.ProgramSection);
   AssertNotNull('Has program section',PasProgram.ProgramSection);
   AssertNotNull('Has initialization section',PasProgram.InitializationSection);
   AssertNotNull('Has initialization section',PasProgram.InitializationSection);
@@ -274,7 +278,7 @@ begin
       raise E;
       raise E;
       end;
       end;
   end;
   end;
-  TAssert.AssertSame('Has resolver',PasResolver,Parser.Engine);
+  TAssert.AssertSame('Has resolver',ResolverEngine,Parser.Engine);
   AssertEquals('Has unit',TPasModule,Module.ClassType);
   AssertEquals('Has unit',TPasModule,Module.ClassType);
   AssertNotNull('Has interface section',Module.InterfaceSection);
   AssertNotNull('Has interface section',Module.InterfaceSection);
   AssertNotNull('Has implementation section',Module.ImplementationSection);
   AssertNotNull('Has implementation section',Module.ImplementationSection);
@@ -588,7 +592,7 @@ var
           begin
           begin
           Ref:=TResolvedReference(El.CustomData);
           Ref:=TResolvedReference(El.CustomData);
           write(' Decl=',GetObjName(Ref.Declaration));
           write(' Decl=',GetObjName(Ref.Declaration));
-          PasResolver.UnmangleSourceLineNumber(Ref.Declaration.SourceLinenumber,aLine,aCol);
+          ResolverEngine.UnmangleSourceLineNumber(Ref.Declaration.SourceLinenumber,aLine,aCol);
           write(Ref.Declaration.SourceFilename,'(',aLine,',',aCol,')');
           write(Ref.Declaration.SourceFilename,'(',aLine,',',aCol,')');
           end
           end
         else
         else
@@ -636,7 +640,7 @@ var
         if El.ClassType=TPasAliasType then
         if El.ClassType=TPasAliasType then
           begin
           begin
           DeclEl:=TPasAliasType(El).DestType;
           DeclEl:=TPasAliasType(El).DestType;
-          PasResolver.UnmangleSourceLineNumber(DeclEl.SourceLinenumber,LabelLine,LabelCol);
+          ResolverEngine.UnmangleSourceLineNumber(DeclEl.SourceLinenumber,LabelLine,LabelCol);
           if (aLabel^.Filename=DeclEl.SourceFilename)
           if (aLabel^.Filename=DeclEl.SourceFilename)
           and (aLabel^.LineNumber=LabelLine)
           and (aLabel^.LineNumber=LabelLine)
           and (aLabel^.StartCol<=LabelCol)
           and (aLabel^.StartCol<=LabelCol)
@@ -841,7 +845,7 @@ var
   Data: PTestResolverReferenceData absolute FindData;
   Data: PTestResolverReferenceData absolute FindData;
   Line, Col: integer;
   Line, Col: integer;
 begin
 begin
-  PasResolver.UnmangleSourceLineNumber(El.SourceLinenumber,Line,Col);
+  ResolverEngine.UnmangleSourceLineNumber(El.SourceLinenumber,Line,Col);
   //writeln('TTestResolver.OnFindReference ',GetObjName(El),' ',El.SourceFilename,' Line=',Line,',Col=',Col,' SearchFile=',Data^.Filename,',Line=',Data^.Line,',Col=',Data^.StartCol,'-',Data^.EndCol);
   //writeln('TTestResolver.OnFindReference ',GetObjName(El),' ',El.SourceFilename,' Line=',Line,',Col=',Col,' SearchFile=',Data^.Filename,',Line=',Data^.Line,',Col=',Data^.StartCol,'-',Data^.EndCol);
   if (Data^.Filename=El.SourceFilename)
   if (Data^.Filename=El.SourceFilename)
   and (Data^.Line=Line)
   and (Data^.Line=Line)
@@ -1417,6 +1421,87 @@ begin
   ParseProgram;
   ParseProgram;
 end;
 end;
 
 
+procedure TTestResolver.TestDuplicateVar;
+var
+  ok: Boolean;
+begin
+  StartProgram(false);
+  Add('var a: longint;');
+  Add('var a: string;');
+  Add('begin');
+  ok:=false;
+  try
+    ParseModule;
+  except
+    on E: EPasResolve do
+      begin
+      AssertEquals('Expected duplicate identifier, but got msg number "'+E.Message+'"',
+        PasResolver.nDuplicateIdentifier,E.MsgNumber);
+      ok:=true;
+      end;
+  end;
+  AssertEquals('duplicate identifier spotted',true,ok);
+end;
+
+procedure TTestResolver.TestRecord;
+begin
+  StartProgram(false);
+  Add('type');
+  Add('  {#TRec}TRec = record');
+  Add('    {#Size}Size: longint;');
+  Add('  end;');
+  Add('var');
+  Add('  {#r}{=TRec}r: TRec;');
+  Add('begin');
+  Add('  {@r}r.{@Size}Size:=3;');
+  ParseProgram;
+end;
+
+procedure TTestResolver.TestRecordVariant;
+begin
+  StartProgram(false);
+  Add('type');
+  Add('  {#TRec}TRec = record');
+  Add('    {#Size}Size: longint;');
+  Add('    case {#vari}vari: longint of');
+  Add('    0: ({#b}b: longint)');
+  Add('  end;');
+  Add('var');
+  Add('  {#r}{=TRec}r: TRec;');
+  Add('begin');
+  Add('  {@r}r.{@Size}Size:=3;');
+  Add('  {@r}r.{@vari}vari:=4;');
+  Add('  {@r}r.{@b}b:=5;');
+  ParseProgram;
+end;
+
+procedure TTestResolver.TestRecordVariantNested;
+begin
+  StartProgram(false);
+  Add('type');
+  Add('  {#TRec}TRec = record');
+  Add('    {#Size}Size: longint;');
+  Add('    case {#vari}vari: longint of');
+  Add('    0: ({#b}b: longint)');
+  Add('    1: ({#c}c:');
+  Add('          record');
+  Add('            {#d}d: longint;');
+  Add('            case {#e}e: longint of');
+  Add('            0: ({#f}f: longint)');
+  Add('          end)');
+  Add('  end;');
+  Add('var');
+  Add('  {#r}{=TRec}r: TRec;');
+  Add('begin');
+  Add('  {@r}r.{@Size}Size:=3;');
+  Add('  {@r}r.{@vari}vari:=4;');
+  Add('  {@r}r.{@b}b:=5;');
+  Add('  {@r}r.{@c}c.{@d}d:=6;');
+  Add('  {@r}r.{@c}c.{@e}e:=7;');
+  Add('  {@r}r.{@c}c.{@f}f:=8;');
+  ParseProgram;
+end;
+
 initialization
 initialization
   RegisterTests([TTestResolver]);
   RegisterTests([TTestResolver]);
 
 

+ 32 - 24
packages/fcl-passrc/tests/tctypeparser.pas

@@ -1191,13 +1191,12 @@ begin
   if HaveVariant then
   if HaveVariant then
     begin
     begin
     AssertNotNull('Have variants',TheRecord.Variants);
     AssertNotNull('Have variants',TheRecord.Variants);
-    AssertNotNull('Have variant type',TheRecord.VariantType);
+    AssertNotNull('Have variant type',TheRecord.VariantEl);
     end
     end
   else
   else
     begin
     begin
     AssertNull('No variants',TheRecord.Variants);
     AssertNull('No variants',TheRecord.Variants);
-    AssertNull('No variant type',TheRecord.VariantType);
-    AssertEquals('No variant name','',TheRecord.VariantName);
+    AssertNull('No variant element',TheRecord.VariantEl);
     end;
     end;
   if AddComment then
   if AddComment then
     AssertComment;
     AssertComment;
@@ -1205,13 +1204,22 @@ end;
 
 
 procedure TTestRecordTypeParser.AssertVariantSelector(AName,AType : string);
 procedure TTestRecordTypeParser.AssertVariantSelector(AName,AType : string);
 
 
+var
+  V: TPasVariable;
 begin
 begin
-  if (AType='') then
-    AType:='Integer';
-  AssertEquals('Have variant selector storage name',AName,TheRecord.VariantName);
-  AssertNotNull('Have variant selector type',TheRecord.VariantType);
-  AssertEquals('Have variant selector type',TPasUnresolvedTypeRef,TheRecord.VariantType.ClassType);
-  AssertEquals('Have variant selector type name',AType,TheRecord.VariantType.Name);
+  AssertNotNull('Have variant element',TheRecord.VariantEl);
+  if AName<>'' then
+    begin
+    AssertEquals('Have variant variable',TPasVariable,TheRecord.VariantEl.ClassType);
+    V:=TPasVariable(TheRecord.VariantEl);
+    AssertEquals('Have variant variable name',AName,V.Name);
+    AssertNotNull('Have variant var type',V.VarType);
+    AssertEquals('Have variant selector type',TPasUnresolvedTypeRef,V.VarType.ClassType);
+    AssertEquals('Have variant selector type name',lowercase(AType),lowercase(V.VarType.Name));
+    end else begin
+    AssertEquals('Have variant selector type',TPasUnresolvedTypeRef,TheRecord.VariantEl.ClassType);
+    AssertEquals('Have variant selector type name',lowercase(AType),lowercase(TheRecord.VariantEl.Name));
+    end;
 end;
 end;
 
 
 procedure TTestRecordTypeParser.AssertConst1(Hints: TPasMemberHints);
 procedure TTestRecordTypeParser.AssertConst1(Hints: TPasMemberHints);
@@ -1316,7 +1324,7 @@ procedure TTestRecordTypeParser.DoTestVariantNoStorage(const AHint: string);
 begin
 begin
   TestFields(['x : integer;','case integer of','0 : (y : integer;)'],AHint,True);
   TestFields(['x : integer;','case integer of','0 : (y : integer;)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertVariant1([]);
   AssertVariant1([]);
 end;
 end;
 
 
@@ -1325,7 +1333,7 @@ procedure TTestRecordTypeParser.DoTestDeprecatedVariantNoStorage(
 begin
 begin
   TestFields(['x : integer;','case integer of','0 : (y : integer deprecated;)'],AHint,True);
   TestFields(['x : integer;','case integer of','0 : (y : integer deprecated;)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertVariant1([hDeprecated]);
   AssertVariant1([hDeprecated]);
 end;
 end;
 
 
@@ -1334,7 +1342,7 @@ procedure TTestRecordTypeParser.DoTestDeprecatedVariantStorage(
 begin
 begin
   TestFields(['x : integer;','case s : integer of','0 : (y : integer deprecated;)'],AHint,True);
   TestFields(['x : integer;','case s : integer of','0 : (y : integer deprecated;)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('s','');
+  AssertVariantSelector('s','integer');
   AssertVariant1([hDeprecated]);
   AssertVariant1([hDeprecated]);
 end;
 end;
 
 
@@ -1342,7 +1350,7 @@ procedure TTestRecordTypeParser.DoTestVariantStorage(const AHint: string);
 begin
 begin
   TestFields(['x : integer;','case s : integer of','0 : (y : integer;)'],AHint,True);
   TestFields(['x : integer;','case s : integer of','0 : (y : integer;)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('s','');
+  AssertVariantSelector('s','integer');
   AssertVariant1([]);
   AssertVariant1([]);
 end;
 end;
 
 
@@ -1350,7 +1358,7 @@ procedure TTestRecordTypeParser.DoTestTwoVariantsNoStorage(const AHint: string);
 begin
 begin
   TestFields(['x : integer;','case integer of','0 : (y : integer;);','1 : (z : integer;)'],AHint,True);
   TestFields(['x : integer;','case integer of','0 : (y : integer;);','1 : (z : integer;)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertVariant1([]);
   AssertVariant1([]);
   AssertVariant2([]);
   AssertVariant2([]);
 end;
 end;
@@ -1359,7 +1367,7 @@ procedure TTestRecordTypeParser.DoTestTwoVariantsStorage(const AHint: string);
 begin
 begin
   TestFields(['x : integer;','case s : integer of','0 : (y : integer;);','1 : (z : integer;)'],AHint,True);
   TestFields(['x : integer;','case s : integer of','0 : (y : integer;);','1 : (z : integer;)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('s','');
+  AssertVariantSelector('s','integer');
   AssertVariant1([]);
   AssertVariant1([]);
   AssertVariant2([]);
   AssertVariant2([]);
 end;
 end;
@@ -1369,7 +1377,7 @@ procedure TTestRecordTypeParser.DoTestTwoVariantsFirstDeprecatedStorage(
 begin
 begin
   TestFields(['x : integer;','case s : integer of','0 : (y : integer deprecated;);','1 : (z : integer;)'],AHint,True);
   TestFields(['x : integer;','case s : integer of','0 : (y : integer deprecated;);','1 : (z : integer;)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('s','');
+  AssertVariantSelector('s','integer');
   AssertVariant1([hdeprecated]);
   AssertVariant1([hdeprecated]);
   AssertVariant2([]);
   AssertVariant2([]);
 end;
 end;
@@ -1379,7 +1387,7 @@ procedure TTestRecordTypeParser.DoTestTwoVariantsSecondDeprecatedStorage(
 begin
 begin
   TestFields(['x : integer;','case s : integer of','0 : (y : integer ;);','1 : (z : integer deprecated;)'],AHint,True);
   TestFields(['x : integer;','case s : integer of','0 : (y : integer ;);','1 : (z : integer deprecated;)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('s','');
+  AssertVariantSelector('s','integer');
   AssertVariant1([]);
   AssertVariant1([]);
   AssertVariant2([hdeprecated]);
   AssertVariant2([hdeprecated]);
 end;
 end;
@@ -1388,7 +1396,7 @@ procedure TTestRecordTypeParser.DoTestVariantTwoLabels(const AHint: string);
 begin
 begin
   TestFields(['x : integer;','case integer of','0,1 : (y : integer)'],AHint,True);
   TestFields(['x : integer;','case integer of','0,1 : (y : integer)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertVariant1([],['0','1']);
   AssertVariant1([],['0','1']);
 end;
 end;
 
 
@@ -1396,7 +1404,7 @@ procedure TTestRecordTypeParser.DoTestTwoVariantsTwoLabels(const AHint: string);
 begin
 begin
   TestFields(['x : integer;','case integer of','0,1 : (y : integer);','2,3 : (z : integer);'],AHint,True);
   TestFields(['x : integer;','case integer of','0,1 : (y : integer);','2,3 : (z : integer);'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertVariant1([],['0','1']);
   AssertVariant1([],['0','1']);
   AssertVariant2([],['2','3']);
   AssertVariant2([],['2','3']);
 end;
 end;
@@ -1405,7 +1413,7 @@ procedure TTestRecordTypeParser.DoTestVariantNestedRecord(const AHint: string);
 begin
 begin
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','end)'],AHint,True);
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','end)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertRecordVariant(0,[],['0']);
   AssertRecordVariant(0,[],['0']);
 end;
 end;
 
 
@@ -1413,7 +1421,7 @@ procedure TTestRecordTypeParser.DoTestVariantNestedVariant(const AHint: string);
 begin
 begin
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','  case byte of ','    1 : (i : integer);','    2 : ( j :  byte)', 'end)'],AHint,True);
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','  case byte of ','    1 : (i : integer);','    2 : ( j :  byte)', 'end)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertRecordVariant(0,[],['0']);
   AssertRecordVariant(0,[],['0']);
   AssertRecordVariantVariant(0,'i','Integer',[],['1']);
   AssertRecordVariantVariant(0,'i','Integer',[],['1']);
   AssertRecordVariantVariant(1,'j','Byte',[],['2'])
   AssertRecordVariantVariant(1,'j','Byte',[],['2'])
@@ -1424,7 +1432,7 @@ procedure TTestRecordTypeParser.DoTestVariantNestedVariantFirstDeprecated(
 begin
 begin
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','  case byte of ','    1 : (i : integer deprecated);','    2 : ( j :  byte)', 'end)'],AHint,True);
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','  case byte of ','    1 : (i : integer deprecated);','    2 : ( j :  byte)', 'end)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertRecordVariant(0,[],['0']);
   AssertRecordVariant(0,[],['0']);
   AssertRecordVariantVariant(0,'i','Integer',[hDeprecated],['1']);
   AssertRecordVariantVariant(0,'i','Integer',[hDeprecated],['1']);
   AssertRecordVariantVariant(1,'j','Byte',[],['2'])
   AssertRecordVariantVariant(1,'j','Byte',[],['2'])
@@ -1435,7 +1443,7 @@ procedure TTestRecordTypeParser.DoTestVariantNestedVariantSecondDeprecated(
 begin
 begin
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','  case byte of ','    1 : (i : integer );','    2 : ( j :  byte deprecated)', 'end)'],AHint,True);
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','  case byte of ','    1 : (i : integer );','    2 : ( j :  byte deprecated)', 'end)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertRecordVariant(0,[],['0']);
   AssertRecordVariant(0,[],['0']);
   AssertRecordVariantVariant(0,'i','Integer',[],['1']);
   AssertRecordVariantVariant(0,'i','Integer',[],['1']);
   AssertRecordVariantVariant(1,'j','Byte',[hDeprecated],['2'])
   AssertRecordVariantVariant(1,'j','Byte',[hDeprecated],['2'])
@@ -1446,7 +1454,7 @@ procedure TTestRecordTypeParser.DoTestVariantNestedVariantBothDeprecated(const A
 begin
 begin
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','  case byte of ','    1 : (i : integer deprecated );','    2 : ( j :  byte deprecated)', 'end)'],AHint,True);
   TestFields(['x : integer;','case integer of','0 : ( y : record','  z : integer;','  case byte of ','    1 : (i : integer deprecated );','    2 : ( j :  byte deprecated)', 'end)'],AHint,True);
   AssertField1([]);
   AssertField1([]);
-  AssertVariantSelector('','');
+  AssertVariantSelector('','integer');
   AssertRecordVariant(0,[],['0']);
   AssertRecordVariant(0,[],['0']);
   AssertRecordVariantVariant(0,'i','Integer',[hdeprecated],['1']);
   AssertRecordVariantVariant(0,'i','Integer',[hdeprecated],['1']);
   AssertRecordVariantVariant(1,'j','Byte',[hDeprecated],['2'])
   AssertRecordVariantVariant(1,'j','Byte',[hDeprecated],['2'])

+ 234 - 75
packages/pastojs/src/fppas2js.pp

@@ -12,9 +12,9 @@
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 
  **********************************************************************
  **********************************************************************
-
+}(*
  Abstract:
  Abstract:
-   Converts a TPasModule into
+   Converts TPasElements into TJSElements.
 
 
  Works:
  Works:
    - units, programs
    - units, programs
@@ -25,20 +25,56 @@
    - procs, params, local vars
    - procs, params, local vars
    - assign statements
    - assign statements
    - function results
    - function results
+   - record types and vars
    - for loop
    - for loop
+   - repeat..until
+   - while..do
+   - try..finally
+   - asm..end
 
 
  ToDos:
  ToDos:
-   - many statements started, needs testing
+   - unit interface function
+   - optional: use $impl
+   - append to for-loop: if($loopend>i)i--;
    - rename overloaded procs, append $0, $1, ...
    - rename overloaded procs, append $0, $1, ...
-   - records
+   - rename js identifiers: apply, bind, call, prototyp, ...
+   - bug: try adds empty line
+   - bug: finally adds unnecessary {}
+   - record const
+   - copy record
+   - asm..end as whole body
    - arrays
    - arrays
-   - access JavaScript from Pascal
+   - classes
+   - passing by reference
+   - procedure modifier external
    - Optional: put implementation into $impl
    - Optional: put implementation into $impl
    - library
    - library
+   - enums, sets. For small sets use an integer, for big sets use
+       var s = {};
+       s["red"] = true; s["green"] = true; s["red"] = true;
+       Object.keys(s).length === 2;
+       s["red"] === true;
+       for (var key in s) // arbitrary order
+         if (s.hasOwnProperty(key))
+           console.log(s[key]);
+   - Fix file names on converter errors (relative instead of full)
+   - 'use strict' to allow javascript compilers optimize better
+   - Avoid nameclashes with the following identifiers:
+      implements, interface, let, package,
+        private, protected, public, static, yield,
+        class, enum, export, extends, import, super,
+        __extends, _super
+      array, Array, null, prototype, delete, for, break, if
+        do, while, constructor, each, in, function, continue, default, arguments,
+        switch, try, catch, throw, var, let, with, return, getPrototypeOf, new,
+        instanceof, Math, Object, anonymous, true, false, null, NaN, undefined,
+        String, Number, static, this, case, default
+   - use UTF8 string literals
+   - dotted unit names
 
 
  Debug flags: -d<x>
  Debug flags: -d<x>
    VerbosePas2JS
    VerbosePas2JS
-}
+*)
 unit fppas2js;
 unit fppas2js;
 
 
 {$mode objfpc}{$H+}
 {$mode objfpc}{$H+}
@@ -68,7 +104,7 @@ resourcestring
   sMemberExprMustBeIdentifier = 'Member expression must be an identifier';
   sMemberExprMustBeIdentifier = 'Member expression must be an identifier';
 
 
 const
 const
-  LoopEndVar = '$loopend';
+  LoopEndVarName = '$loopend';
 
 
 Type
 Type
 
 
@@ -102,9 +138,9 @@ Type
   TInitializationContext = Class(TConvertContext)
   TInitializationContext = Class(TConvertContext)
   end;
   end;
 
 
-  { TProcContext }
+  { TDeclContext }
 
 
-  TProcContext = Class(TConvertContext)
+  TDeclContext = Class(TConvertContext)
   end;
   end;
 
 
   { TProcBodyContext }
   { TProcBodyContext }
@@ -131,7 +167,7 @@ Type
     Procedure DoError(Const Msg : String);
     Procedure DoError(Const Msg : String);
     Procedure DoError(Const Msg : String; Const Args : Array of Const);
     Procedure DoError(Const Msg : String; Const Args : Array of Const);
     Procedure DoError(MsgNumber: integer; const MsgPattern: string; Const Args : Array of Const; El: TPasElement);
     Procedure DoError(MsgNumber: integer; const MsgPattern: string; Const Args : Array of Const; El: TPasElement);
-    procedure RaiseNotSupported(El: TPasElement; AContext: TConvertContext);
+    procedure RaiseNotSupported(El: TPasElement; AContext: TConvertContext; const Msg: string = '');
     procedure RaiseIdentifierNotFound(Identifier: string; El: TPasElement);
     procedure RaiseIdentifierNotFound(Identifier: string; El: TPasElement);
     procedure RaiseInconsistency;
     procedure RaiseInconsistency;
     // Never create an element manually, always use the below function
     // Never create an element manually, always use the below function
@@ -157,6 +193,8 @@ Type
       Add: TJSElement; Src: TPasElement);
       Add: TJSElement; Src: TPasElement);
     Function CreateValInit(PasType: TPasType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement;virtual;
     Function CreateValInit(PasType: TPasType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement;virtual;
     Function CreateVarInit(El: TPasVariable; AContext: TConvertContext): TJSElement;virtual;
     Function CreateVarInit(El: TPasVariable; AContext: TConvertContext): TJSElement;virtual;
+    Function CreateRecordInit(aRecord: TPasRecordType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement;virtual;
+    Function CreateTypeRef(El: TPasType; AContext : TConvertContext): TJSElement;virtual;
     // Statements
     // Statements
     Function ConvertImplBlockElements(El: TPasImplBlock; AContext: TConvertContext): TJSElement;virtual;
     Function ConvertImplBlockElements(El: TPasImplBlock; AContext: TConvertContext): TJSElement;virtual;
     Function ConvertBeginEndStatement(El: TPasImplBeginBlock; AContext: TConvertContext): TJSElement;virtual;
     Function ConvertBeginEndStatement(El: TPasImplBeginBlock; AContext: TConvertContext): TJSElement;virtual;
@@ -175,6 +213,7 @@ Type
     Function ConvertTryFinallyStatement(El: TPasImplTryFinally; AContext: TConvertContext): TJSElement;virtual;
     Function ConvertTryFinallyStatement(El: TPasImplTryFinally; AContext: TConvertContext): TJSElement;virtual;
     Function ConvertExceptOn(El: TPasImplExceptOn; AContext: TConvertContext): TJSElement;
     Function ConvertExceptOn(El: TPasImplExceptOn; AContext: TConvertContext): TJSElement;
     Function ConvertTryExceptStatement(El: TPasImplTryExcept; AContext: TConvertContext): TJSElement;
     Function ConvertTryExceptStatement(El: TPasImplTryExcept; AContext: TConvertContext): TJSElement;
+    Function ConvertAsmStatement(El: TPasImplAsmStatement; AContext: TConvertContext): TJSElement;
     Procedure CreateInitSection(El: TPasModule; Src: TJSSourceElements; AContext: TConvertContext);
     Procedure CreateInitSection(El: TPasModule; Src: TJSSourceElements; AContext: TConvertContext);
     // Expressions
     // Expressions
     Function ConvertArrayValues(El: TArrayValues; AContext : TConvertContext): TJSElement;virtual;
     Function ConvertArrayValues(El: TArrayValues; AContext : TConvertContext): TJSElement;virtual;
@@ -210,9 +249,10 @@ Type
     Function ConvertType(El: TPasElement; AContext : TConvertContext): TJSElement;virtual;
     Function ConvertType(El: TPasElement; AContext : TConvertContext): TJSElement;virtual;
     Function ConvertVariable(El: TPasVariable; AContext : TConvertContext): TJSElement;virtual;
     Function ConvertVariable(El: TPasVariable; AContext : TConvertContext): TJSElement;virtual;
     Function ConvertElement(El : TPasElement; AContext : TConvertContext) : TJSElement; virtual;
     Function ConvertElement(El : TPasElement; AContext : TConvertContext) : TJSElement; virtual;
-    function ConvertClassType(El: TPasClassType;AContext: TConvertContext): TJSElement;
-    Function ConvertClassMember(El: TPasElement;AContext: TConvertContext): TJSElement;
-    Function ConvertClassConstructor(El: TPasConstructor;AContext: TConvertContext): TJSElement;
+    function ConvertRecordType(El: TPasRecordType; AContext: TConvertContext): TJSElement; virtual;
+    function ConvertClassType(El: TPasClassType; AContext: TConvertContext): TJSElement; virtual;
+    Function ConvertClassMember(El: TPasElement; AContext: TConvertContext): TJSElement; virtual;
+    Function ConvertClassConstructor(El: TPasConstructor; AContext: TConvertContext): TJSElement; virtual;
   Public
   Public
     constructor Create;
     constructor Create;
     Function ConvertPasElement(El : TPasElement; Resolver: TPasResolver) : TJSElement;
     Function ConvertPasElement(El : TPasElement; Resolver: TPasResolver) : TJSElement;
@@ -335,9 +375,7 @@ begin
     UsesSection:=TPasLibrary(El).LibrarySection
     UsesSection:=TPasLibrary(El).LibrarySection
   else
   else
     UsesSection:=El.InterfaceSection;
     UsesSection:=El.InterfaceSection;
-  UsesList:=nil;
-  if UsesSection<>nil then
-    UsesList:=UsesSection.UsesList;
+  UsesList:=UsesSection.UsesList;
   ArgArray.Elements.AddElement.Expr:=CreateUsesList(UsesList,AContext);
   ArgArray.Elements.AddElement.Expr:=CreateUsesList(UsesList,AContext);
 
 
   // add parameter: function(){}
   // add parameter: function(){}
@@ -426,7 +464,7 @@ function TPasToJSConverter.ConvertCallExpression(El: TParamsExpr;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 begin
 begin
   if AContext=nil then ;
   if AContext=nil then ;
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertCallExpression');
   Result:=nil;
   Result:=nil;
 end;
 end;
 
 
@@ -717,7 +755,9 @@ begin
     else
     else
       begin
       begin
       Name:=TransformVariableName(Decl,AContext);
       Name:=TransformVariableName(Decl,AContext);
+      {$IFDEF VerbosePas2JS}
       writeln('TPasToJSConverter.ConvertIdentifierExpr Decl.Parent=',GetObjName(Decl.Parent));
       writeln('TPasToJSConverter.ConvertIdentifierExpr Decl.Parent=',GetObjName(Decl.Parent));
+      {$ENDIF}
       if Decl.Parent is TPasSection then
       if Decl.Parent is TPasSection then
         begin
         begin
         FoundModule:=Decl.GetModule;
         FoundModule:=Decl.GetModule;
@@ -896,7 +936,7 @@ begin
   else if (El is TRecordValues) then
   else if (El is TRecordValues) then
     Result:=ConvertRecordValues(TRecordValues(El),AContext)
     Result:=ConvertRecordValues(TRecordValues(El),AContext)
   else
   else
-    RaiseNotSupported(El,AContext);
+    RaiseNotSupported(El,AContext,'ConvertExpression');
 end;
 end;
 
 
 function TPasToJSConverter.CreateConstDecl(El: TPasConst;
 function TPasToJSConverter.CreateConstDecl(El: TPasConst;
@@ -931,8 +971,10 @@ function TPasToJSConverter.CreateTypeDecl(El: TPasType;
 begin
 begin
   Result:=Nil;
   Result:=Nil;
   if (El is TPasClassType) then
   if (El is TPasClassType) then
-    Result := ConvertClassType(TPasClassType(El), AContext);
-  // ToDo: Need to do something for classes and records.
+    Result := ConvertClassType(TPasClassType(El), AContext)
+  else if El is TPasRecordType then
+    Result := ConvertRecordType(TPasRecordType(El), AContext);
+  // other types don't need a constructor function
 end;
 end;
 
 
 function TPasToJSConverter.CreateVarDecl(El: TPasVariable;
 function TPasToJSConverter.CreateVarDecl(El: TPasVariable;
@@ -1015,7 +1057,7 @@ begin
   IsProcBody:=(El is TProcedureBody) and (TProcedureBody(El).Body<>nil);
   IsProcBody:=(El is TProcedureBody) and (TProcedureBody(El).Body<>nil);
   IsFunction:=IsProcBody and (El.Parent is TPasFunction);
   IsFunction:=IsProcBody and (El.Parent is TPasFunction);
 
 
-  SubContext:=TProcContext.Create(aContext);
+  SubContext:=TDeclContext.Create(aContext);
   try
   try
     SubContext.Element:=El;
     SubContext.Element:=El;
 
 
@@ -1035,7 +1077,7 @@ begin
       else if P is TPasProcedure then
       else if P is TPasProcedure then
         E:=ConvertProcedure(TPasProcedure(P),SubContext)
         E:=ConvertProcedure(TPasProcedure(P),SubContext)
       else
       else
-        RaiseNotSupported(P as TPasElement,AContext);
+        RaiseNotSupported(P as TPasElement,AContext,'ConvertDeclarations');
       if (Pos('.', P.Name) > 0) then
       if (Pos('.', P.Name) > 0) then
         AddProcedureToClass(TJSStatementList(Result), E, P as TPasProcedure)
         AddProcedureToClass(TJSStatementList(Result), E, P as TPasProcedure)
       else
       else
@@ -1071,7 +1113,7 @@ function TPasToJSConverter.ConvertType(El: TPasElement;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertType');
   Result:=Nil;
   Result:=Nil;
 {
 {
   ToDo:
   ToDo:
@@ -1254,7 +1296,7 @@ begin
     FD.Name:=TJSString(FunName);
     FD.Name:=TJSString(FunName);
   FS.AFunction:=FD;
   FS.AFunction:=FD;
   for n := 0 to El.ProcType.Args.Count - 1 do
   for n := 0 to El.ProcType.Args.Count - 1 do
-    FD.Params.Add(TransformVariableName(TPasArgument(El.ProcType.Args[0]).Name,AContext));
+    FD.Params.Add(TransformVariableName(TPasArgument(El.ProcType.Args[n]).Name,AContext));
   FD.Body:=TJSFunctionBody(CreateElement(TJSFunctionBody,El.Body));
   FD.Body:=TJSFunctionBody(CreateElement(TJSFunctionBody,El.Body));
 
 
   SubContext:=TProcBodyContext.Create(AContext);
   SubContext:=TProcBodyContext.Create(AContext);
@@ -1288,33 +1330,27 @@ function TPasToJSConverter.ConvertImplBlockElements(El: TPasImplBlock;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 var
 var
-  B : TJSElement;
-  S,S2 : TJSStatementList;
+  First, Last: TJSStatementList;
   I : Integer;
   I : Integer;
+  PasImpl: TPasImplElement;
+  JSImpl : TJSElement;
 
 
 begin
 begin
   if Not (Assigned(El.Elements) and (El.Elements.Count>0)) then
   if Not (Assigned(El.Elements) and (El.Elements.Count>0)) then
     Result:=TJSEmptyBlockStatement(CreateElement(TJSEmptyBlockStatement,El))
     Result:=TJSEmptyBlockStatement(CreateElement(TJSEmptyBlockStatement,El))
   else
   else
     begin
     begin
-    S:=TJSStatementList(CreateElement(TJSStatementList,TPasImplElement(El)));
-    Result:=S;
+    First:=nil;
+    Result:=First;
+    Last:=First;
+    //writeln('TPasToJSConverter.ConvertImplBlockElements START El.Elements.Count=',El.Elements.Count);
     For I:=0 to El.Elements.Count-1 do
     For I:=0 to El.Elements.Count-1 do
       begin
       begin
-      B:=ConvertElement(TPasImplElement(El.Elements[i]),AContext);
-      if not Assigned(S.A) then
-        S.A:=B
-      else
-        begin
-        if Assigned(S.B) then
-          begin
-          S2:=TJSStatementList(CreateElement(TJSStatementList,TPasImplElement(El.Elements[i])));
-          S2.A:=S.B;
-          S.B:=S2;
-          S:=S2;
-          end;
-        S.B:=B;
-        end;
+      PasImpl:=TPasImplElement(El.Elements[i]);
+      JSImpl:=ConvertElement(PasImpl,AContext);
+      //writeln('TPasToJSConverter.ConvertImplBlockElements ',i,' ',JSImpl.ClassName);
+      AddToStatementList(First,Last,JSImpl,PasImpl);
+      Result:=First;
       end;
       end;
     end;
     end;
 end;
 end;
@@ -1345,8 +1381,11 @@ begin
     AssignSt.Expr:=FDS;
     AssignSt.Expr:=FDS;
     FD:=TJSFuncDef.Create;
     FD:=TJSFuncDef.Create;
     FDS.AFunction:=FD;
     FDS.AFunction:=FD;
-    FD.Body:=TJSFunctionBody(CreateElement(TJSFunctionBody,El));
-    FD.Body.A:=ConvertImplBlockElements(El,AContext);
+    if El.Elements.Count>0 then
+      begin
+      FD.Body:=TJSFunctionBody(CreateElement(TJSFunctionBody,El));
+      FD.Body.A:=ConvertImplBlockElements(El,AContext);
+      end;
     ok:=true;
     ok:=true;
   finally
   finally
     if not ok then FreeAndNil(Result);
     if not ok then FreeAndNil(Result);
@@ -1366,11 +1405,12 @@ function TPasToJSConverter.ConvertTryStatement(El: TPasImplTry;
 Var
 Var
   B,F : TJSElement;
   B,F : TJSElement;
   T : TJSTryStatement;
   T : TJSTryStatement;
-  IsFin : Boolean;
+  IsFin , ok: Boolean;
 
 
 begin
 begin
   F:=Nil;
   F:=Nil;
   B:=ConvertImplBlockElements(El,AContext);
   B:=ConvertImplBlockElements(El,AContext);
+  ok:=false;
   try
   try
     F:=ConvertElement(El.FinallyExcept,AContext);
     F:=ConvertElement(El.FinallyExcept,AContext);
     IsFin:=El.FinallyExcept is TPasImplTryFinally;
     IsFin:=El.FinallyExcept is TPasImplTryFinally;
@@ -1381,10 +1421,13 @@ begin
       T:=TJSTryCatchStatement(CreateElement(TJSTryCatchStatement,El));
       T:=TJSTryCatchStatement(CreateElement(TJSTryCatchStatement,El));
       T.Ident:=TJSString(GetExceptionObjectName(AContext));
       T.Ident:=TJSString(GetExceptionObjectName(AContext));
       end;
       end;
-  except
-    FreeAndNil(B);
-    FreeAndNil(F);
-    Raise;
+    ok:=true;
+  finally
+    if not ok then
+      begin
+      B.Free;
+      F.Free;
+      end;
   end;
   end;
   if IsFin then
   if IsFin then
     T.BFinally:=F
     T.BFinally:=F
@@ -1397,7 +1440,6 @@ end;
 function TPasToJSConverter.ConvertTryFinallyStatement(El: TPasImplTryFinally;
 function TPasToJSConverter.ConvertTryFinallyStatement(El: TPasImplTryFinally;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
-
 begin
 begin
   Result:=ConvertImplBlockElements(El,AContext);
   Result:=ConvertImplBlockElements(El,AContext);
 end;
 end;
@@ -1405,11 +1447,27 @@ end;
 function TPasToJSConverter.ConvertTryExceptStatement(El: TPasImplTryExcept;
 function TPasToJSConverter.ConvertTryExceptStatement(El: TPasImplTryExcept;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
-
 begin
 begin
   Result:=ConvertImplBlockElements(El,AContext);
   Result:=ConvertImplBlockElements(El,AContext);
 end;
 end;
 
 
+function TPasToJSConverter.ConvertAsmStatement(El: TPasImplAsmStatement;
+  AContext: TConvertContext): TJSElement;
+var
+  pex: TJSPrimaryExpressionIdent;
+  s: String;
+begin
+  if AContext=nil then ;
+  s:=El.Tokens.Text;
+  if s='' then
+    Result:=TJSEmptyStatement(CreateElement(TJSEmptyStatement,El))
+  else begin
+    pex:=TJSPrimaryExpressionIdent(CreateElement(TJSPrimaryExpressionIdent,El));
+    pex.Name := TJSString(s);
+    Result:=pex;
+  end;
+end;
+
 procedure TPasToJSConverter.CreateInitSection(El: TPasModule;
 procedure TPasToJSConverter.CreateInitSection(El: TPasModule;
   Src: TJSSourceElements; AContext: TConvertContext);
   Src: TJSSourceElements; AContext: TConvertContext);
 var
 var
@@ -1434,9 +1492,9 @@ function TPasToJSConverter.ConvertImplBlock(El: TPasImplBlock;
 
 
 begin
 begin
   Result:=Nil;
   Result:=Nil;
-  if (EL is TPasImplStatement) then
+  if (El is TPasImplStatement) then
     Result:=ConvertStatement(TPasImplStatement(El),AContext)
     Result:=ConvertStatement(TPasImplStatement(El),AContext)
-  else if (EL is TPasImplIfElse) then
+  else if (El is TPasImplIfElse) then
     Result:=ConvertIfStatement(TPasImplIfElse(El),AContext)
     Result:=ConvertIfStatement(TPasImplIfElse(El),AContext)
   else if (El is TPasImplRepeatUntil) then
   else if (El is TPasImplRepeatUntil) then
     Result:=ConvertRepeatStatement(TPasImplRepeatUntil(El),AContext)
     Result:=ConvertRepeatStatement(TPasImplRepeatUntil(El),AContext)
@@ -1470,7 +1528,7 @@ function TPasToJSConverter.ConvertPackage(El: TPasPackage;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertPackage');
   Result:=Nil;
   Result:=Nil;
   // ToDo TPasPackage = class(TPasElement)
   // ToDo TPasPackage = class(TPasElement)
 end;
 end;
@@ -1479,7 +1537,7 @@ function TPasToJSConverter.ConvertResString(El: TPasResString;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertResString');
   Result:=Nil;
   Result:=Nil;
   // ToDo: TPasResString
   // ToDo: TPasResString
 end;
 end;
@@ -1488,7 +1546,8 @@ function TPasToJSConverter.ConvertArgument(El: TPasArgument;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  // is this still needed?
+  RaiseNotSupported(El,AContext,'ConvertArgument');
   Result:=Nil;
   Result:=Nil;
   // ToDo: TPasArgument
   // ToDo: TPasArgument
 end;
 end;
@@ -1519,7 +1578,8 @@ function TPasToJSConverter.ConvertConst(El: TPasConst; AContext: TConvertContext
   ): TJSElement;
   ): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  // is this still needed?
+  RaiseNotSupported(El,AContext,'ConvertConst');
   Result:=Nil;
   Result:=Nil;
   // ToDo: TPasConst
   // ToDo: TPasConst
 end;
 end;
@@ -1528,7 +1588,7 @@ function TPasToJSConverter.ConvertProperty(El: TPasProperty;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertProperty');
   Result:=Nil;
   Result:=Nil;
   // ToDo: TPasProperty = class(TPasVariable)
   // ToDo: TPasProperty = class(TPasVariable)
 end;
 end;
@@ -1537,7 +1597,7 @@ function TPasToJSConverter.ConvertExportSymbol(El: TPasExportSymbol;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertExportSymbol');
   Result:=Nil;
   Result:=Nil;
   // ToDo: TPasExportSymbol
   // ToDo: TPasExportSymbol
 end;
 end;
@@ -1546,7 +1606,7 @@ function TPasToJSConverter.ConvertLabels(El: TPasLabels;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertLabels');
   Result:=Nil;
   Result:=Nil;
   // ToDo: TPasLabels = class(TPasImplElement)
   // ToDo: TPasLabels = class(TPasImplElement)
 end;
 end;
@@ -1597,7 +1657,7 @@ function TPasToJSConverter.ConvertCommand(El: TPasImplCommand;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertCommand');
   Result:=Nil;
   Result:=Nil;
   // ToDo: TPasImplCommand = class(TPasImplElement)
   // ToDo: TPasImplCommand = class(TPasImplElement)
 end;
 end;
@@ -1749,7 +1809,7 @@ begin
     VarStat:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
     VarStat:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
     VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El));
     VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El));
     VarStat.A:=VarDecl;
     VarStat.A:=VarDecl;
-    VarDecl.Name:=LoopEndVar;
+    VarDecl.Name:=LoopEndVarName;
     VarDecl.Init:=ConvertElement(El.EndExpr,AContext);
     VarDecl.Init:=ConvertElement(El.EndExpr,AContext);
     ForSt.Init:=VarStat;
     ForSt.Init:=VarStat;
     // add "LoopVar<=$loopend"
     // add "LoopVar<=$loopend"
@@ -1758,7 +1818,7 @@ begin
     else
     else
       BinExp:=TJSRelationalExpressionLE(CreateElement(TJSRelationalExpressionLE,El.EndExpr));
       BinExp:=TJSRelationalExpressionLE(CreateElement(TJSRelationalExpressionLE,El.EndExpr));
     BinExp.A:=ConvertElement(El.VariableName,AContext);
     BinExp.A:=ConvertElement(El.VariableName,AContext);
-    BinExp.B:=CreateIdentifierExpr(LoopEndVar,El.EndExpr);
+    BinExp.B:=CreateIdentifierExpr(LoopEndVarName,El.EndExpr);
     ForSt.Cond:=BinExp;
     ForSt.Cond:=BinExp;
     // add "LoopVar++"
     // add "LoopVar++"
     If El.Down then
     If El.Down then
@@ -2059,36 +2119,41 @@ begin
     else
     else
       begin
       begin
       // merge lists (append)
       // merge lists (append)
-      if Last.B<>nil then
-        raise Exception.Create('internal error: TPasToJSConverter.ConvertDeclarations.AddToStatementList add list');
-      Last.B:=Add;
-      while Last.B is TJSStatementList do
-        Last:=TJSStatementList(Last.B);
       if Last.B<>nil then
       if Last.B<>nil then
         begin
         begin
+        // add a nil to the end of chain
         SL2:=TJSStatementList(CreateElement(TJSStatementList,Src));
         SL2:=TJSStatementList(CreateElement(TJSStatementList,Src));
         SL2.A:=Last.B;
         SL2.A:=Last.B;
         Last.B:=SL2;
         Last.B:=SL2;
         Last:=SL2;
         Last:=SL2;
+        // Last.B is now nil
         end;
         end;
+      Last.B:=Add;
+      while Last.B is TJSStatementList do
+        Last:=TJSStatementList(Last.B);
       end;
       end;
     end
     end
   else
   else
     begin
     begin
     if Last=nil then
     if Last=nil then
       begin
       begin
+      // start list
       Last:=TJSStatementList(CreateElement(TJSStatementList,Src));
       Last:=TJSStatementList(CreateElement(TJSStatementList,Src));
       First:=Last;
       First:=Last;
+      Last.A:=Add;
       end
       end
+    else if Last.B=nil then
+      // second element
+      Last.B:=Add
     else
     else
       begin
       begin
-      if Last.B<>nil then
-        raise Exception.Create('internal error: TPasToJSConverter.ConvertDeclarations.AddToStatementList add element');
+      // add to chain
       SL2:=TJSStatementList(CreateElement(TJSStatementList,Src));
       SL2:=TJSStatementList(CreateElement(TJSStatementList,Src));
+      SL2.A:=Last.B;
       Last.B:=SL2;
       Last.B:=SL2;
       Last:=SL2;
       Last:=SL2;
+      Last.B:=Add;
       end;
       end;
-    Last.A:=Add;
     end;
     end;
 end;
 end;
 
 
@@ -2105,6 +2170,8 @@ begin
     If Assigned(Expr) then
     If Assigned(Expr) then
       DoError(nInitializedArraysNotSupported,sInitializedArraysNotSupported,[],PasType);
       DoError(nInitializedArraysNotSupported,sInitializedArraysNotSupported,[],PasType);
     end
     end
+  else if T is TPasRecordType then
+    Result:=CreateRecordInit(TPasRecordType(T),Expr,El,AContext)
   else if Assigned(Expr) then
   else if Assigned(Expr) then
     Result:=ConvertElement(Expr,AContext)
     Result:=ConvertElement(Expr,AContext)
   else
   else
@@ -2156,6 +2223,42 @@ begin
   Result:=CreateValInit(El.VarType,El.Expr,El,AContext);
   Result:=CreateValInit(El.VarType,El.Expr,El,AContext);
 end;
 end;
 
 
+function TPasToJSConverter.CreateRecordInit(aRecord: TPasRecordType;
+  Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement;
+var
+  NewMemE: TJSNewMemberExpression;
+begin
+  if Expr<>nil then
+    RaiseNotSupported(Expr,AContext,'CreateRecordInit Expr<>nil');
+  NewMemE:=TJSNewMemberExpression(CreateElement(TJSNewMemberExpression,El));
+  Result:=NewMemE;
+  NewMemE.MExpr:=CreateTypeRef(aRecord,AContext);
+end;
+
+function TPasToJSConverter.CreateTypeRef(El: TPasType; AContext: TConvertContext
+  ): TJSElement;
+var
+  FoundModule: TPasModule;
+  Name: String;
+begin
+  Name:=TransformVariableName(El.Name,AContext);
+  { $IFDEF VerbosePas2JS}
+  writeln('TPasToJSConverter.CreateTypeRef El="',GetObjName(El),'" El.Parent=',GetObjName(El.Parent));
+  { $ENDIF}
+  if El.Parent is TPasSection then
+    begin
+    FoundModule:=El.GetModule;
+    if FoundModule=nil then
+      RaiseInconsistency;
+    if AContext.GetRootModule=FoundModule then
+      Name:='this.'+Name
+    else
+      Name:='pas.'+TransformModuleName(FoundModule,AContext)+'.'+Name;
+    end;
+  // ToDo: use TJSDotMemberExpression for dots
+  Result:=CreateIdentifierExpr(Name,El);
+end;
+
 function TPasToJSConverter.CreateProcedureDeclaration(const El: TPasElement):
 function TPasToJSConverter.CreateProcedureDeclaration(const El: TPasElement):
 TJSFunctionDeclarationStatement;
 TJSFunctionDeclarationStatement;
 var
 var
@@ -2169,6 +2272,7 @@ begin
   FS.AFunction := FD;
   FS.AFunction := FD;
   Result := FS;
   Result := FS;
 end;
 end;
+
 function TPasToJSConverter.ConvertExceptOn(El: TPasImplExceptOn;
 function TPasToJSConverter.ConvertExceptOn(El: TPasImplExceptOn;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
@@ -2215,8 +2319,10 @@ begin
     Result:=ConvertExceptOn(TPasImplExceptOn(El),AContext)
     Result:=ConvertExceptOn(TPasImplExceptOn(El),AContext)
   else if (El is TPasImplForLoop) then
   else if (El is TPasImplForLoop) then
     Result:=ConvertForStatement(TPasImplForLoop(El),AContext)
     Result:=ConvertForStatement(TPasImplForLoop(El),AContext)
+  else if (El is TPasImplAsmStatement) then
+    Result:=ConvertAsmStatement(TPasImplAsmStatement(El),AContext)
   else
   else
-    RaiseNotSupported(El,AContext);
+    RaiseNotSupported(El,AContext,'ConvertStatement');
 {
 {
   TPasImplCaseStatement = class(TPasImplStatement)
   TPasImplCaseStatement = class(TPasImplStatement)
 }
 }
@@ -2227,7 +2333,7 @@ function TPasToJSConverter.ConvertCommands(El: TPasImplCommands;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertCommands');
   Result:=Nil;
   Result:=Nil;
   // ToDo: TPasImplCommands = class(TPasImplElement)
   // ToDo: TPasImplCommands = class(TPasImplElement)
 end;
 end;
@@ -2236,7 +2342,7 @@ function TPasToJSConverter.ConvertLabelMark(El: TPasImplLabelMark;
   AContext: TConvertContext): TJSElement;
   AContext: TConvertContext): TJSElement;
 
 
 begin
 begin
-  RaiseNotSupported(El,AContext);
+  RaiseNotSupported(El,AContext,'ConvertLabelMark');
   Result:=Nil;
   Result:=Nil;
   // ToDo:   TPasImplLabelMark = class(TPasImplLabelMark) then
   // ToDo:   TPasImplLabelMark = class(TPasImplLabelMark) then
 end;
 end;
@@ -2284,6 +2390,57 @@ begin
     Result:=nil;
     Result:=nil;
 end;
 end;
 
 
+function TPasToJSConverter.ConvertRecordType(El: TPasRecordType;
+  AContext: TConvertContext): TJSElement;
+(*
+  type
+    TMyRecord = record
+      i: longint;
+      s: string;
+      d: double;
+    end;
+
+    this.TMyRecord=function() {
+                 i=0;
+                 s="";
+                 d=0.0;
+                };
+*)
+var
+  AssignSt: TJSSimpleAssignStatement;
+  ok: Boolean;
+  i: Integer;
+  PasVar: TPasVariable;
+  FDS: TJSFunctionDeclarationStatement;
+  FD: TJSFuncDef;
+  JSVar: TJSElement;
+  First, Last: TJSStatementList;
+begin
+  AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
+  Result:=AssignSt;
+  ok:=false;
+  try
+    AssignSt.LHS:=CreateMemberExpression(['this',TransformVariableName(El.Name,AContext)]);
+    FDS:=TJSFunctionDeclarationStatement(CreateElement(TJSFunctionDeclarationStatement,El));
+    AssignSt.Expr:=FDS;
+    FD:=TJSFuncDef.Create;
+    FDS.AFunction:=FD;
+    FD.Body:=TJSFunctionBody(CreateElement(TJSFunctionBody,El));
+    First:=nil;
+    Last:=nil;
+    for i:=0 to El.Members.Count-1 do
+      begin
+      PasVar:=TPasVariable(El.Members[i]);
+      JSVar:=ConvertVariable(PasVar,AContext);
+      AddToStatementList(First,Last,JSVar,PasVar);
+      FD.Body.A:=First;
+      end;
+    ok:=true;
+  finally
+    if not ok then FreeAndNil(Result);
+  end;
+end;
+
 procedure TPasToJSConverter.DoError(const Msg: String);
 procedure TPasToJSConverter.DoError(const Msg: String);
 begin
 begin
   Raise EPas2JS.Create(Msg);
   Raise EPas2JS.Create(Msg);
@@ -2308,12 +2465,14 @@ begin
 end;
 end;
 
 
 procedure TPasToJSConverter.RaiseNotSupported(El: TPasElement;
 procedure TPasToJSConverter.RaiseNotSupported(El: TPasElement;
-  AContext: TConvertContext);
+  AContext: TConvertContext; const Msg: string);
 var
 var
   E: EPas2JS;
   E: EPas2JS;
 begin
 begin
   if AContext=nil then ;
   if AContext=nil then ;
   E:=EPas2JS.CreateFmt(sPasElementNotSupported,[GetObjName(El)]);
   E:=EPas2JS.CreateFmt(sPasElementNotSupported,[GetObjName(El)]);
+  if Msg<>'' then
+    E.Message:=E.Message+': '+Msg;
   E.PasElement:=El;
   E.PasElement:=El;
   E.MsgNumber:=nPasElementNotSupported;
   E.MsgNumber:=nPasElementNotSupported;
   SetLength(E.Args,1);
   SetLength(E.Args,1);

+ 15 - 10
packages/pastojs/tests/tcconverter.pp

@@ -333,8 +333,13 @@ begin
   AssertEquals('Correct condition class',TJSUnaryNotExpression,E.Cond.ClassType);
   AssertEquals('Correct condition class',TJSUnaryNotExpression,E.Cond.ClassType);
   AssertIdentifier('Conditional expression',TJSUnaryNotExpression(E.Cond).A,'a');
   AssertIdentifier('Conditional expression',TJSUnaryNotExpression(E.Cond).A,'a');
   L:=AssertListStatement('Multiple statements',E.Body);
   L:=AssertListStatement('Multiple statements',E.Body);
+  //  writeln('TTestStatementConverter.TestRepeatUntilStatementTwo L.A=',L.A.ClassName);
+  // writeln('  L.B=',L.B.ClassName);
+  // writeln('  L.B.A=',TJSStatementList(L.B).A.ClassName);
+  // writeln('  L.B.B=',TJSStatementList(L.B).B.ClassName);
+
   AssertAssignStatement('First List statement is assignment',L.A,'b','c');
   AssertAssignStatement('First List statement is assignment',L.A,'b','c');
-  AssertAssignStatement('Second List statement is assignment',L.b,'d','e');
+  AssertAssignStatement('Second List statement is assignment',L.B,'d','e');
 end;
 end;
 
 
 Procedure TTestStatementConverter.TestRepeatUntilStatementThree;
 Procedure TTestStatementConverter.TestRepeatUntilStatementThree;
@@ -394,15 +399,15 @@ begin
   E:=TJSForStatement(AssertElement('Second in list is "for" statement',TJSForStatement,L.B));
   E:=TJSForStatement(AssertElement('Second in list is "for" statement',TJSForStatement,L.B));
 
 
   // "var $loopend=100"
   // "var $loopend=100"
-  VS:=TJSVariableStatement(AssertElement('var '+LoopEndVar,TJSVariableStatement,E.Init));
-  VD:=TJSVarDeclaration(AssertElement('var '+LoopEndVar,TJSVarDeclaration,VS.A));
-  AssertEquals('Correct name for '+LoopEndVar,LoopEndVar,VD.Name);
+  VS:=TJSVariableStatement(AssertElement('var '+LoopEndVarName,TJSVariableStatement,E.Init));
+  VD:=TJSVarDeclaration(AssertElement('var '+LoopEndVarName,TJSVarDeclaration,VS.A));
+  AssertEquals('Correct name for '+LoopEndVarName,LoopEndVarName,VD.Name);
   AssertLiteral('Correct end value',VD.Init,100);
   AssertLiteral('Correct end value',VD.Init,100);
 
 
   // i<=$loopend
   // i<=$loopend
   C:=TJSRelationalExpressionLE(AssertElement('Condition is <= expression',TJSRelationalExpressionLE,E.Cond));
   C:=TJSRelationalExpressionLE(AssertElement('Condition is <= expression',TJSRelationalExpressionLE,E.Cond));
   AssertIdentifier('Cond LHS is loop variable',C.A,'i');
   AssertIdentifier('Cond LHS is loop variable',C.A,'i');
-  AssertIdentifier('Cond RHS is '+LoopEndVar,C.B,LoopEndVar);
+  AssertIdentifier('Cond RHS is '+LoopEndVarName,C.B,LoopEndVarName);
 
 
   // i++
   // i++
   I:=TJSUnaryPostPlusPlusExpression(AssertElement('Increment is ++ statement',TJSUnaryPostPlusPlusExpression,E.Incr));
   I:=TJSUnaryPostPlusPlusExpression(AssertElement('Increment is ++ statement',TJSUnaryPostPlusPlusExpression,E.Incr));
@@ -444,15 +449,15 @@ begin
   E:=TJSForStatement(AssertElement('Second in list is "for" statement',TJSForStatement,L.B));
   E:=TJSForStatement(AssertElement('Second in list is "for" statement',TJSForStatement,L.B));
 
 
   // "var $loopend=1"
   // "var $loopend=1"
-  VS:=TJSVariableStatement(AssertElement('var '+LoopEndVar,TJSVariableStatement,E.Init));
-  VD:=TJSVarDeclaration(AssertElement('var '+LoopEndVar,TJSVarDeclaration,VS.A));
-  AssertEquals('Correct name for '+LoopEndVar,LoopEndVar,VD.Name);
+  VS:=TJSVariableStatement(AssertElement('var '+LoopEndVarName,TJSVariableStatement,E.Init));
+  VD:=TJSVarDeclaration(AssertElement('var '+LoopEndVarName,TJSVarDeclaration,VS.A));
+  AssertEquals('Correct name for '+LoopEndVarName,LoopEndVarName,VD.Name);
   AssertLiteral('Correct end value',VD.Init,1);
   AssertLiteral('Correct end value',VD.Init,1);
 
 
   // i>=$loopend
   // i>=$loopend
   C:=TJSRelationalExpressionGE(AssertElement('Condition is >= expression',TJSRelationalExpressionGE,E.Cond));
   C:=TJSRelationalExpressionGE(AssertElement('Condition is >= expression',TJSRelationalExpressionGE,E.Cond));
   AssertIdentifier('Cond LHS is loop variable',C.A,'i');
   AssertIdentifier('Cond LHS is loop variable',C.A,'i');
-  AssertIdentifier('Cond RHS is '+LoopEndVar,C.B,LoopEndVar);
+  AssertIdentifier('Cond RHS is '+LoopEndVarName,C.B,LoopEndVarName);
 
 
   // i--
   // i--
   I:=TJSUnaryPostMinusMinusExpression(AssertElement('Increment is -- statement',TJSUnaryPostMinusMinusExpression,E.Incr));
   I:=TJSUnaryPostMinusMinusExpression(AssertElement('Increment is -- statement',TJSUnaryPostMinusMinusExpression,E.Incr));
@@ -1333,7 +1338,7 @@ Class Procedure TTestConverter.AssertAssignStatement(Const Msg : String; El : TJ
 begin
 begin
   AssertNotNull(Msg+': have statement',EL);
   AssertNotNull(Msg+': have statement',EL);
   If not (El is TJSSimpleAssignStatement) then
   If not (El is TJSSimpleAssignStatement) then
-    Fail(Msg+': statement is not assign statement but is'+El.ClassName);
+    Fail(Msg+': statement is not assign statement but is '+El.ClassName);
   AssertIdentifier(Msg+': left hand side ('+LHS+')',TJSAssignStatement(EL).LHS,LHS);
   AssertIdentifier(Msg+': left hand side ('+LHS+')',TJSAssignStatement(EL).LHS,LHS);
   AssertIdentifier(Msg+': left hand side ('+LHS+')',TJSAssignStatement(EL).Expr,RHS);
   AssertIdentifier(Msg+': left hand side ('+LHS+')',TJSAssignStatement(EL).Expr,RHS);
 end;
 end;

+ 952 - 0
packages/pastojs/tests/tcmodules.pas

@@ -0,0 +1,952 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2014 by Michael Van Canneyt
+
+    Unit tests for Pascal-to-Javascript converter class.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************
+
+ Examples:
+    ./testpas2js --suite=TTestModuleConverter.TestEmptyProgram
+}
+unit tcmodules;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testregistry, contnrs, fppas2js,
+  pastree, PScanner, PasResolver, PParser, jstree, jswriter, jsbase;
+
+const
+  po_pas2js = [po_asmwhole,po_resolvestandardtypes];
+type
+
+  { TTestPasParser }
+
+  TTestPasParser = Class(TPasParser)
+  end;
+
+  TOnFindUnit = function(const aUnitName: String): TPasModule of object;
+
+  { TTestEnginePasResolver }
+
+  TTestEnginePasResolver = class(TPasResolver)
+  private
+    FFilename: string;
+    FModule: TPasModule;
+    FOnFindUnit: TOnFindUnit;
+    FParser: TTestPasParser;
+    FResolver: TStreamResolver;
+    FScanner: TPascalScanner;
+    FSource: string;
+    procedure SetModule(AValue: TPasModule);
+  public
+    constructor Create;
+    destructor Destroy; override;
+    function FindModule(const AName: String): TPasModule; override;
+    property OnFindUnit: TOnFindUnit read FOnFindUnit write FOnFindUnit;
+    property Filename: string read FFilename write FFilename;
+    property Resolver: TStreamResolver read FResolver write FResolver;
+    property Scanner: TPascalScanner read FScanner write FScanner;
+    property Parser: TTestPasParser read FParser write FParser;
+    property Source: string read FSource write FSource;
+    property Module: TPasModule read FModule write SetModule;
+  end;
+
+  { TTestModule }
+
+  TTestModule = Class(TTestCase)
+  private
+    FConverter: TPasToJSConverter;
+    FEngine: TTestEnginePasResolver;
+    FFilename: string;
+    FFileResolver: TStreamResolver;
+    FJSInitBody: TJSFunctionBody;
+    FJSInterfaceUses: TJSArrayLiteral;
+    FJSModule: TJSSourceElements;
+    FJSModuleSrc: TJSSourceElements;
+    FJSSource: TStringList;
+    FModule: TPasModule;
+    FJSModuleCallArgs: TJSArguments;
+    FModules: TObjectList;// list of TTestEnginePasResolver
+    FParser: TTestPasParser;
+    FPasProgram: TPasProgram;
+    FJSRegModuleCall: TJSCallExpression;
+    FScanner: TPascalScanner;
+    FSource: TStringList;
+    FFirstPasStatement: TPasImplBlock;
+    function GetModuleCount: integer;
+    function GetModules(Index: integer): TTestEnginePasResolver;
+    function OnPasResolverFindUnit(const aUnitName: String): TPasModule;
+  protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+    Procedure Add(Line: string);
+    Procedure StartParsing;
+    Procedure ParseModule;
+    procedure ParseProgram;
+  protected
+    function FindModuleWithFilename(aFilename: string): TTestEnginePasResolver;
+    function AddModule(aFilename: string): TTestEnginePasResolver;
+    function AddModuleWithSrc(aFilename, Src: string): TTestEnginePasResolver;
+    function AddModuleWithIntfImplSrc(aFilename, InterfaceSrc,
+      ImplementationSrc: string): TTestEnginePasResolver;
+    procedure AddSystemUnit;
+    procedure StartProgram(NeedSystemUnit: boolean);
+    Procedure ConvertProgram;
+    procedure CheckDottedIdentifier(Msg: string; El: TJSElement; DottedName: string);
+    function GetDottedIdentifier(El: TJSElement): string;
+    procedure CheckSource(Msg,Statements, InitStatements: string);
+    procedure CheckDiff(Msg, Expected, Actual: string);
+    property PasProgram: TPasProgram Read FPasProgram;
+    property Modules[Index: integer]: TTestEnginePasResolver read GetModules;
+    property ModuleCount: integer read GetModuleCount;
+    property Engine: TTestEnginePasResolver read FEngine;
+    property Filename: string read FFilename;
+    Property Module: TPasModule Read FModule;
+    property FirstPasStatement: TPasImplBlock read FFirstPasStatement;
+    property Converter: TPasToJSConverter read FConverter;
+    property JSSource: TStringList read FJSSource;
+    property JSModule: TJSSourceElements read FJSModule;
+    property JSRegModuleCall: TJSCallExpression read FJSRegModuleCall;
+    property JSModuleCallArgs: TJSArguments read FJSModuleCallArgs;
+    property JSInterfaceUses: TJSArrayLiteral read FJSInterfaceUses;
+    property JSModuleSrc: TJSSourceElements read FJSModuleSrc;
+    property JSInitBody: TJSFunctionBody read FJSInitBody;
+  public
+    property Source: TStringList read FSource;
+    property FileResolver: TStreamResolver read FFileResolver;
+    property Scanner: TPascalScanner read FScanner;
+    property Parser: TTestPasParser read FParser;
+  Published
+    Procedure TestEmptyProgram;
+    Procedure TestVarInt;
+    Procedure TestEmptyProc;
+    Procedure TestProcTwoArgs;
+    Procedure TestFunctionInt;
+    Procedure TestFunctionString;
+    Procedure TestVarRecord;
+    Procedure TestForLoop;
+    Procedure TestForLoopInFunction;
+    Procedure TestRepeatUntil;
+    Procedure TestAsmBlock;
+    Procedure TestTryFinally;
+  end;
+
+function LinesToStr(Args: array of const): string;
+function ExtractFileUnitName(aFilename: string): string;
+function JSToStr(El: TJSElement): string;
+
+implementation
+
+function LinesToStr(Args: array of const): string;
+var
+  s: String;
+  i: Integer;
+begin
+  s:='';
+  for i:=Low(Args) to High(Args) do
+    case Args[i].VType of
+      vtChar:         s += Args[i].VChar+LineEnding;
+      vtString:       s += Args[i].VString^+LineEnding;
+      vtPChar:        s += Args[i].VPChar+LineEnding;
+      vtWideChar:     s += AnsiString(Args[i].VWideChar)+LineEnding;
+      vtPWideChar:    s += AnsiString(Args[i].VPWideChar)+LineEnding;
+      vtAnsiString:   s += AnsiString(Args[i].VAnsiString)+LineEnding;
+      vtWidestring:   s += AnsiString(WideString(Args[i].VWideString))+LineEnding;
+      vtUnicodeString:s += AnsiString(UnicodeString(Args[i].VUnicodeString))+LineEnding;
+    end;
+  Result:=s;
+end;
+
+function ExtractFileUnitName(aFilename: string): string;
+var
+  p: Integer;
+begin
+  Result:=ExtractFileName(aFilename);
+  if Result='' then exit;
+  for p:=length(Result) downto 1 do
+    case Result[p] of
+    '/','\': exit;
+    '.':
+      begin
+      Delete(Result,p,length(Result));
+      exit;
+      end;
+    end;
+end;
+
+function JSToStr(El: TJSElement): string;
+var
+  aWriter: TBufferWriter;
+  aJSWriter: TJSWriter;
+begin
+  aWriter:=TBufferWriter.Create(1000);
+  try
+    aJSWriter:=TJSWriter.Create(aWriter);
+    aJSWriter.IndentSize:=2;
+    aJSWriter.WriteJS(El);
+    Result:=aWriter.AsAnsistring;
+  finally
+    aWriter.Free;
+  end;
+end;
+
+{ TTestEnginePasResolver }
+
+procedure TTestEnginePasResolver.SetModule(AValue: TPasModule);
+begin
+  if FModule=AValue then Exit;
+  if Module<>nil then
+    Module.Release;
+  FModule:=AValue;
+  if Module<>nil then
+    Module.AddRef;
+end;
+
+constructor TTestEnginePasResolver.Create;
+begin
+  inherited Create;
+  StoreSrcColumns:=true;
+end;
+
+destructor TTestEnginePasResolver.Destroy;
+begin
+  FreeAndNil(FResolver);
+  Module:=nil;
+  FreeAndNil(FParser);
+  FreeAndNil(FScanner);
+  FreeAndNil(FResolver);
+  inherited Destroy;
+end;
+
+function TTestEnginePasResolver.FindModule(const AName: String): TPasModule;
+begin
+  Result:=nil;
+  if Assigned(OnFindUnit) then
+    Result:=OnFindUnit(AName);
+end;
+
+{ TTestModule }
+
+function TTestModule.GetModuleCount: integer;
+begin
+  Result:=FModules.Count;
+end;
+
+function TTestModule.GetModules(Index: integer
+  ): TTestEnginePasResolver;
+begin
+  Result:=TTestEnginePasResolver(FModules[Index]);
+end;
+
+function TTestModule.OnPasResolverFindUnit(const aUnitName: String
+  ): TPasModule;
+var
+  i: Integer;
+  CurEngine: TTestEnginePasResolver;
+  CurUnitName: String;
+begin
+  //writeln('TTestModule.OnPasResolverFindUnit START Unit="',aUnitName,'"');
+  Result:=nil;
+  for i:=0 to ModuleCount-1 do
+    begin
+    CurEngine:=Modules[i];
+    CurUnitName:=ExtractFileUnitName(CurEngine.Filename);
+    //writeln('TTestModule.OnPasResolverFindUnit Checking ',i,'/',ModuleCount,' ',CurEngine.Filename,' ',CurUnitName);
+    if CompareText(aUnitName,CurUnitName)=0 then
+      begin
+      Result:=CurEngine.Module;
+      if Result<>nil then exit;
+      //writeln('TTestModule.OnPasResolverFindUnit PARSING unit "',CurEngine.Filename,'"');
+      FileResolver.FindSourceFile(aUnitName);
+
+      CurEngine.Resolver:=TStreamResolver.Create;
+      CurEngine.Resolver.OwnsStreams:=True;
+      //writeln('TTestResolver.OnPasResolverFindUnit SOURCE=',CurEngine.Source);
+      CurEngine.Resolver.AddStream(CurEngine.FileName,TStringStream.Create(CurEngine.Source));
+      CurEngine.Scanner:=TPascalScanner.Create(CurEngine.Resolver);
+      CurEngine.Parser:=TTestPasParser.Create(CurEngine.Scanner,CurEngine.Resolver,CurEngine);
+      CurEngine.Parser.Options:=CurEngine.Parser.Options+po_pas2js;
+      if CompareText(CurUnitName,'System')=0 then
+        CurEngine.Parser.ImplicitUses.Clear;
+      CurEngine.Scanner.OpenFile(CurEngine.Filename);
+      try
+        CurEngine.Parser.NextToken;
+        CurEngine.Parser.ParseUnit(CurEngine.FModule);
+      except
+        on E: Exception do
+          begin
+          writeln('ERROR: TTestModule.OnPasResolverFindUnit during parsing: '+E.ClassName+':'+E.Message
+            +' File='+CurEngine.Scanner.CurFilename
+            +' LineNo='+IntToStr(CurEngine.Scanner.CurRow)
+            +' Col='+IntToStr(CurEngine.Scanner.CurColumn)
+            +' Line="'+CurEngine.Scanner.CurLine+'"'
+            );
+          raise E;
+          end;
+      end;
+      //writeln('TTestModule.OnPasResolverFindUnit END ',CurUnitName);
+      Result:=CurEngine.Module;
+      exit;
+      end;
+    end;
+  writeln('TTestModule.OnPasResolverFindUnit missing unit "',aUnitName,'"');
+  raise Exception.Create('can''t find unit "'+aUnitName+'"');
+end;
+
+procedure TTestModule.SetUp;
+begin
+  inherited SetUp;
+  FSource:=TStringList.Create;
+  FModules:=TObjectList.Create(true);
+
+  FFilename:='test1.pp';
+  FFileResolver:=TStreamResolver.Create;
+  FFileResolver.OwnsStreams:=True;
+  FScanner:=TPascalScanner.Create(FFileResolver);
+  FEngine:=AddModule(Filename);
+  FParser:=TTestPasParser.Create(FScanner,FFileResolver,FEngine);
+  Parser.Options:=Parser.Options+po_pas2js;
+  FModule:=Nil;
+  FConverter:=TPasToJSConverter.Create;
+end;
+
+procedure TTestModule.TearDown;
+begin
+  FJSModule:=nil;
+  FJSRegModuleCall:=nil;
+  FJSModuleCallArgs:=nil;
+  FJSInterfaceUses:=nil;
+  FJSModuleSrc:=nil;
+  FJSInitBody:=nil;
+  FreeAndNil(FJSSource);
+  FreeAndNil(FJSModule);
+  FreeAndNil(FConverter);
+  Engine.Clear;
+  if Assigned(FModule) then
+    begin
+    FModule.Release;
+    FModule:=nil;
+    end;
+  FreeAndNil(FSource);
+  FreeAndNil(FParser);
+  FreeAndNil(FScanner);
+  FreeAndNil(FFileResolver);
+  if FModules<>nil then
+    begin
+    FreeAndNil(FModules);
+    FEngine:=nil;
+    end;
+
+  inherited TearDown;
+end;
+
+procedure TTestModule.Add(Line: string);
+begin
+  Source.Add(Line);
+end;
+
+procedure TTestModule.StartParsing;
+begin
+  FileResolver.AddStream(FileName,TStringStream.Create(Source.Text));
+  Scanner.OpenFile(FileName);
+  Writeln('// Test : ',Self.TestName);
+  Writeln(Source.Text);
+end;
+
+procedure TTestModule.ParseModule;
+begin
+  StartParsing;
+  Parser.ParseMain(FModule);
+  AssertNotNull('Module resulted in Module',FModule);
+  AssertEquals('modulename',ChangeFileExt(FFileName,''),Module.Name);
+end;
+
+procedure TTestModule.ParseProgram;
+begin
+  FFirstPasStatement:=nil;
+  try
+    ParseModule;
+  except
+    on E: EParserError do
+      begin
+      writeln('ERROR: TTestModule.ParseProgram Parser: '+E.ClassName+':'+E.Message
+        +' File='+Scanner.CurFilename
+        +' LineNo='+IntToStr(Scanner.CurRow)
+        +' Col='+IntToStr(Scanner.CurColumn)
+        +' Line="'+Scanner.CurLine+'"'
+        );
+      raise E;
+      end;
+    on E: EPasResolve do
+      begin
+      writeln('ERROR: TTestModule.ParseProgram PasResolver: '+E.ClassName+':'+E.Message
+        +' File='+Scanner.CurFilename
+        +' LineNo='+IntToStr(Scanner.CurRow)
+        +' Col='+IntToStr(Scanner.CurColumn)
+        +' Line="'+Scanner.CurLine+'"'
+        );
+      raise E;
+      end;
+    on E: Exception do
+      begin
+      writeln('ERROR: TTestModule.ParseProgram Exception: '+E.ClassName+':'+E.Message);
+      raise E;
+      end;
+  end;
+  TAssert.AssertSame('Has resolver',Engine,Parser.Engine);
+  AssertEquals('Has program',TPasProgram,Module.ClassType);
+  FPasProgram:=TPasProgram(Module);
+  AssertNotNull('Has program section',PasProgram.ProgramSection);
+  AssertNotNull('Has initialization section',PasProgram.InitializationSection);
+  if (PasProgram.InitializationSection.Elements.Count>0) then
+    if TObject(PasProgram.InitializationSection.Elements[0]) is TPasImplBlock then
+      FFirstPasStatement:=TPasImplBlock(PasProgram.InitializationSection.Elements[0]);
+end;
+
+function TTestModule.FindModuleWithFilename(aFilename: string
+  ): TTestEnginePasResolver;
+var
+  i: Integer;
+begin
+  for i:=0 to ModuleCount-1 do
+    if CompareText(Modules[i].Filename,aFilename)=0 then
+      exit(Modules[i]);
+  Result:=nil;
+end;
+
+function TTestModule.AddModule(aFilename: string
+  ): TTestEnginePasResolver;
+begin
+  //writeln('TTestModuleConverter.AddModule ',aFilename);
+  if FindModuleWithFilename(aFilename)<>nil then
+    raise Exception.Create('TTestModuleConverter.AddModule: file "'+aFilename+'" already exists');
+  Result:=TTestEnginePasResolver.Create;
+  Result.Filename:=aFilename;
+  Result.AddObjFPCBuiltInIdentifiers([btChar,btString,btLongint,btInt64,btBoolean,btDouble]);
+  Result.OnFindUnit:=@OnPasResolverFindUnit;
+  FModules.Add(Result);
+end;
+
+function TTestModule.AddModuleWithSrc(aFilename, Src: string
+  ): TTestEnginePasResolver;
+begin
+  Result:=AddModule(aFilename);
+  Result.Source:=Src;
+end;
+
+function TTestModule.AddModuleWithIntfImplSrc(aFilename, InterfaceSrc,
+  ImplementationSrc: string): TTestEnginePasResolver;
+var
+  Src: String;
+begin
+  Src:='unit '+ExtractFileUnitName(aFilename)+';'+LineEnding;
+  Src+=LineEnding;
+  Src+='interface'+LineEnding;
+  Src+=LineEnding;
+  Src+=InterfaceSrc;
+  Src+='implementation'+LineEnding;
+  Src+=LineEnding;
+  Src+=ImplementationSrc;
+  Src+='end.'+LineEnding;
+  Result:=AddModuleWithSrc(aFilename,Src);
+end;
+
+procedure TTestModule.AddSystemUnit;
+begin
+  AddModuleWithIntfImplSrc('system.pp',
+    // interface
+    LinesToStr([
+    'type',
+    '  integer=longint;',
+    'var',
+    '  ExitCode: Longint;',
+    ''
+    // implementation
+    ]),LinesToStr([
+    ''
+    ]));
+end;
+
+procedure TTestModule.StartProgram(NeedSystemUnit: boolean);
+begin
+  if NeedSystemUnit then
+    AddSystemUnit
+  else
+    Parser.ImplicitUses.Clear;
+  Add('program test1;');
+  Add('');
+end;
+
+procedure TTestModule.ConvertProgram;
+var
+  ModuleNameExpr: TJSLiteral;
+  FunDecl, InitFunction: TJSFunctionDeclarationStatement;
+  FunDef: TJSFuncDef;
+  InitAssign: TJSSimpleAssignStatement;
+  FunBody: TJSFunctionBody;
+begin
+  FJSSource:=TStringList.Create;
+  Add('end.');
+  ParseProgram;
+  FJSModule:=FConverter.ConvertPasElement(Module,nil) as TJSSourceElements;
+  FJSSource.Text:=JSToStr(JSModule);
+  writeln('TTestModule.ConvertProgram JS:');
+  write(FJSSource.Text);
+
+  // rtl.module(...
+  AssertEquals('jsmodule has one statement - the call',1,JSModule.Statements.Count);
+  AssertNotNull('register module call',JSModule.Statements.Nodes[0].Node);
+  AssertEquals('register module call',TJSCallExpression,JSModule.Statements.Nodes[0].Node.ClassType);
+  FJSRegModuleCall:=JSModule.Statements.Nodes[0].Node as TJSCallExpression;
+  AssertNotNull('register module rtl.module expr',JSRegModuleCall.Expr);
+  AssertNotNull('register module rtl.module args',JSRegModuleCall.Args);
+  AssertEquals('rtl.module args',TJSArguments,JSRegModuleCall.Args.ClassType);
+  FJSModuleCallArgs:=JSRegModuleCall.Args as TJSArguments;
+  AssertEquals('rtl.module args.count',3,JSModuleCallArgs.Elements.Count);
+
+  // parameter 'unitname'
+  AssertNotNull('module name param',JSModuleCallArgs.Elements.Elements[0].Expr);
+  ModuleNameExpr:=JSModuleCallArgs.Elements.Elements[0].Expr as TJSLiteral;
+  AssertEquals('module name param is string',ord(jstString),ord(ModuleNameExpr.Value.ValueType));
+  AssertEquals('module name','program',String(ModuleNameExpr.Value.AsString));
+
+  // main uses section
+  AssertNotNull('interface uses section',JSModuleCallArgs.Elements.Elements[1].Expr);
+  AssertEquals('interface uses section type',TJSArrayLiteral,JSModuleCallArgs.Elements.Elements[1].Expr.ClassType);
+  FJSInterfaceUses:=JSModuleCallArgs.Elements.Elements[1].Expr as TJSArrayLiteral;
+
+  // function()
+  AssertNotNull('module function',JSModuleCallArgs.Elements.Elements[2].Expr);
+  AssertEquals('module function type',TJSFunctionDeclarationStatement,JSModuleCallArgs.Elements.Elements[2].Expr.ClassType);
+  FunDecl:=JSModuleCallArgs.Elements.Elements[2].Expr as TJSFunctionDeclarationStatement;
+  AssertNotNull('module function def',FunDecl.AFunction);
+  FunDef:=FunDecl.AFunction as TJSFuncDef;
+  AssertEquals('module function name','',String(FunDef.Name));
+  AssertNotNull('module function body',FunDef.Body);
+  FunBody:=FunDef.Body as TJSFunctionBody;
+  FJSModuleSrc:=FunBody.A as TJSSourceElements;
+
+  // init this.$main - the last statement
+  AssertEquals('this.$main function 1',true,JSModuleSrc.Statements.Count>0);
+  InitAssign:=JSModuleSrc.Statements.Nodes[JSModuleSrc.Statements.Count-1].Node as TJSSimpleAssignStatement;
+  CheckDottedIdentifier('init function',InitAssign.LHS,'this.$main');
+
+  InitFunction:=InitAssign.Expr as TJSFunctionDeclarationStatement;
+  FJSInitBody:=InitFunction.AFunction.Body as TJSFunctionBody;
+end;
+
+procedure TTestModule.CheckDottedIdentifier(Msg: string; El: TJSElement;
+  DottedName: string);
+begin
+  if DottedName='' then
+    begin
+    AssertNull(Msg,El);
+    end
+  else
+    begin
+    AssertNotNull(Msg,El);
+    AssertEquals(Msg,DottedName,GetDottedIdentifier(EL));
+    end;
+end;
+
+function TTestModule.GetDottedIdentifier(El: TJSElement): string;
+begin
+  if El=nil then
+    Result:=''
+  else if El is TJSPrimaryExpressionIdent then
+    Result:=String(TJSPrimaryExpressionIdent(El).Name)
+  else if El is TJSDotMemberExpression then
+    Result:=GetDottedIdentifier(TJSDotMemberExpression(El).MExpr)+'.'+String(TJSDotMemberExpression(El).Name)
+  else
+    AssertEquals('GetDottedIdentifier',TJSPrimaryExpressionIdent,El.ClassType);
+end;
+
+procedure TTestModule.CheckSource(Msg, Statements, InitStatements: string);
+var
+  ActualSrc, ExpectedSrc: String;
+begin
+  ActualSrc:=JSToStr(JSModuleSrc);
+  ExpectedSrc:=Statements+LineEnding
+    +'this.$main = function () {'+LineEnding
+    +InitStatements
+    +'};'+LineEnding;
+  CheckDiff(Msg,ExpectedSrc,ActualSrc);
+end;
+
+procedure TTestModule.CheckDiff(Msg, Expected, Actual: string);
+// search diff, ignore changes in spaces
+const
+  SpaceChars = [#9,#10,#13,' '];
+var
+  ExpectedP, ActualP: PChar;
+
+  function FindLineEnd(p: PChar): PChar;
+  begin
+    Result:=p;
+    while not (Result^ in [#0,#10,#13]) do inc(Result);
+  end;
+
+  function FindLineStart(p, MinP: PChar): PChar;
+  begin
+    while (p>MinP) and not (p[-1] in [#10,#13]) do dec(p);
+    Result:=p;
+  end;
+
+  procedure DiffFound;
+  var
+    ActLineStartP, ActLineEndP, p, StartPos: PChar;
+    ExpLine, ActLine: String;
+    i: Integer;
+  begin
+    writeln('Diff found "',Msg,'". Lines:');
+    // write correct lines
+    p:=PChar(Expected);
+    repeat
+      StartPos:=p;
+      while not (p^ in [#0,#10,#13]) do inc(p);
+      ExpLine:=copy(Expected,StartPos-PChar(Expected)+1,p-StartPos);
+      if p^ in [#10,#13] then begin
+        if (p[1] in [#10,#13]) and (p^<>p[1]) then
+          inc(p,2)
+        else
+          inc(p);
+      end;
+      if p<=ExpectedP then begin
+        writeln('= ',ExpLine);
+      end else begin
+        // diff line
+        // write actual line
+        ActLineStartP:=FindLineStart(ActualP,PChar(Actual));
+        ActLineEndP:=FindLineEnd(ActualP);
+        ActLine:=copy(Actual,ActLineStartP-PChar(Actual)+1,ActLineEndP-ActLineStartP);
+        writeln('- ',ActLine);
+        // write expected line
+        writeln('+ ',ExpLine);
+        // write empty line with pointer ^
+        for i:=1 to 2+ExpectedP-StartPos do write(' ');
+        writeln('^');
+        AssertEquals(Msg,ExpLine,ActLine);
+        break;
+      end;
+    until p^=#0;
+    raise Exception.Create('diff found, but lines are the same, internal error');
+  end;
+
+var
+  IsSpaceNeeded: Boolean;
+  LastChar: Char;
+begin
+  if Expected='' then Expected:=' ';
+  if Actual='' then Actual:=' ';
+  ExpectedP:=PChar(Expected);
+  ActualP:=PChar(Actual);
+  repeat
+    //writeln('TTestModule.CheckDiff Exp="',ExpectedP^,'" Act="',ActualP^,'"');
+    case ExpectedP^ of
+    #0:
+      begin
+      // check that rest of Actual has only spaces
+      while ActualP^ in SpaceChars do inc(ActualP);
+      if ActualP^<>#0 then
+        DiffFound;
+      exit;
+      end;
+    ' ',#9,#10,#13:
+      begin
+      // skip space in Expected
+      IsSpaceNeeded:=false;
+      if ExpectedP>PChar(Expected) then
+        LastChar:=ExpectedP[-1]
+      else
+        LastChar:=#0;
+      while ExpectedP^ in SpaceChars do inc(ExpectedP);
+      if (LastChar in ['a'..'z','A'..'Z','0'..'9','_','$'])
+          and (ExpectedP^ in ['a'..'z','A'..'Z','0'..'9','_','$']) then
+        IsSpaceNeeded:=true;
+      if IsSpaceNeeded and (not (ActualP^ in SpaceChars)) then
+        DiffFound;
+      while ActualP^ in SpaceChars do inc(ActualP);
+      end;
+    else
+      while ActualP^ in SpaceChars do inc(ActualP);
+      if ExpectedP^<>ActualP^ then
+        DiffFound;
+      inc(ExpectedP);
+      inc(ActualP);
+    end;
+  until false;
+end;
+
+procedure TTestModule.TestEmptyProgram;
+begin
+  StartProgram(false);
+  Add('begin');
+  ConvertProgram;
+  CheckSource('Empty program','','');
+end;
+
+procedure TTestModule.TestVarInt;
+begin
+  StartProgram(false);
+  Add('var i: longint;');
+  Add('begin');
+  ConvertProgram;
+  CheckSource('TestVarInt','this.i=0;','');
+end;
+
+procedure TTestModule.TestEmptyProc;
+begin
+  StartProgram(false);
+  Add('procedure Test;');
+  Add('begin');
+  Add('end;');
+  Add('begin');
+  ConvertProgram;
+  CheckSource('TestEmptyProc',
+    LinesToStr([ // statements
+    'this.test = function () {',
+    '};'
+    ]),
+    LinesToStr([ // this.$main
+    ''
+    ]));
+end;
+
+procedure TTestModule.TestProcTwoArgs;
+begin
+  StartProgram(false);
+  Add('procedure Test(a,b: longint);');
+  Add('begin');
+  Add('end;');
+  Add('begin');
+  ConvertProgram;
+  CheckSource('TestProcTwoArgs',
+    LinesToStr([ // statements
+    'this.test = function (a,b) {',
+    '};'
+    ]),
+    LinesToStr([ // this.$main
+    ''
+    ]));
+end;
+
+procedure TTestModule.TestFunctionInt;
+begin
+  StartProgram(false);
+  Add('function Test(a: longint): longint;');
+  Add('begin');
+  Add('  Result:=2*a');
+  Add('end;');
+  Add('begin');
+  ConvertProgram;
+  CheckSource('TestProcTwoArgs',
+    LinesToStr([ // statements
+    'this.test = function (a) {',
+    '  var result = 0;',
+    '  result = (2*a);',
+    '  return result;',
+    '};'
+    ]),
+    LinesToStr([ // this.$main
+    ''
+    ]));
+end;
+
+procedure TTestModule.TestFunctionString;
+begin
+  StartProgram(false);
+  Add('function Test(a: string): string;');
+  Add('begin');
+  Add('  Result:=a+a');
+  Add('end;');
+  Add('begin');
+  ConvertProgram;
+  CheckSource('TestProcTwoArgs',
+    LinesToStr([ // statements
+    'this.test = function (a) {',
+    '  var result = "";',
+    '  result = (a+a);',
+    '  return result;',
+    '};'
+    ]),
+    LinesToStr([ // this.$main
+    ''
+    ]));
+end;
+
+procedure TTestModule.TestVarRecord;
+begin
+  StartProgram(false);
+  Add('type');
+  Add('  TRecA = record');
+  Add('    B: longint;');
+  Add('  end;');
+  Add('var r: TRecA;');
+  Add('begin');
+  Add('  r.B:=123');
+  ConvertProgram;
+  CheckSource('TestVarRecord',
+    LinesToStr([ // statements
+    'this.treca = function () {',
+    '  b = 0;',
+    '};',
+    'this.r = new this.treca();'
+    ]),
+    LinesToStr([ // this.$main
+    'this.r.b = 123;'
+    ]));
+end;
+
+procedure TTestModule.TestForLoop;
+begin
+  StartProgram(false);
+  Add('var');
+  Add('  i, j, n: longint;');
+  Add('begin');
+  Add('  j:=0;');
+  Add('  n:=3;');
+  Add('  for i:=1 to n do');
+  Add('  begin');
+  Add('    j:=j+i;');
+  Add('  end;');
+  ConvertProgram;
+  CheckSource('TestVarRecord',
+    LinesToStr([ // statements
+    'this.i = 0;',
+    'this.j = 0;',
+    'this.n = 0;'
+    ]),
+    LinesToStr([ // this.$main
+    '  this.j = 0;',
+    '  this.n = 3;',
+    '  this.i = 1;',
+    '  for (var $loopend = this.n; (this.i <= $loopend); this.i++) {',
+    '    this.j = (this.j + this.i);',
+    '  };'
+    ]));
+end;
+
+procedure TTestModule.TestForLoopInFunction;
+begin
+  StartProgram(false);
+  Add('function SumNumbers(n: longint): longint;');
+  Add('var');
+  Add('  i, j: longint;');
+  Add('begin');
+  Add('  j:=0;');
+  Add('  for i:=1 to n do');
+  Add('  begin');
+  Add('    j:=j+i;');
+  Add('  end;');
+  Add('end;');
+  Add('begin');
+  Add('  SumNumbers(3);');
+  ConvertProgram;
+  CheckSource('TestVarRecord',
+    LinesToStr([ // statements
+    'this.sumnumbers = function (n) {',
+    '  var result = 0;',
+    '  var i = 0;',
+    '  var j = 0;',
+    '  j = 0;',
+    '  i = 1;',
+    '  for (var $loopend = n; (i <= $loopend); i++) {',
+    '    j = (j + i);',
+    '  };',
+    '  return result;',
+    '};'
+    ]),
+    LinesToStr([ // this.$main
+    '  this.sumnumbers(3);'
+    ]));
+end;
+
+procedure TTestModule.TestRepeatUntil;
+begin
+  StartProgram(false);
+  Add('var');
+  Add('  i, j, n: longint;');
+  Add('begin');
+  Add('  n:=3;');
+  Add('  j:=0;');
+  Add('  i:=0;');
+  Add('  repeat');
+  Add('    i:=i+1;');
+  Add('    j:=j+i;');
+  Add('  until i>=n');
+  ConvertProgram;
+  CheckSource('TestVarRecord',
+    LinesToStr([ // statements
+    'this.i = 0;',
+    'this.j = 0;',
+    'this.n = 0;'
+    ]),
+    LinesToStr([ // this.$main
+    '  this.n = 3;',
+    '  this.j = 0;',
+    '  this.i = 0;',
+    '  do{',
+    '    this.i = (this.i + 1);',
+    '    this.j = (this.j + this.i);',
+    '  }while(!(this.i>=this.n));'
+    ]));
+end;
+
+procedure TTestModule.TestAsmBlock;
+begin
+  StartProgram(false);
+  Add('var');
+  Add('  i: longint;');
+  Add('begin');
+  Add('  i:=1;');
+  Add('  asm');
+  Add('    if (i==1) {');
+  Add('      i=2;');
+  Add('    }');
+  Add('    if (i==2){ i=3; }');
+  Add('  end;');
+  Add('  i:=4;');
+  ConvertProgram;
+  CheckSource('TestAsm',
+    LinesToStr([ // statements
+    'this.i = 0;'
+    ]),
+    LinesToStr([ // this.$main
+    '  this.i = 1;',
+    'if (i==1) {',
+    'i=2;',
+    '}',
+    'if (i==2){ i=3; }',
+    ';',
+    'this.i = 4;'
+    ]));
+end;
+
+procedure TTestModule.TestTryFinally;
+begin
+  StartProgram(false);
+  Add('var i: longint;');
+  Add('begin');
+  Add('  try');
+  Add('    i:=0; i:=2 div i;');
+  Add('  finally');
+  Add('    i:=3');
+  Add('  end;');
+  ConvertProgram;
+end;
+
+Initialization
+  RegisterTests([TTestModule]);
+end.
+

+ 1 - 1
packages/pastojs/tests/testpas2js.pp

@@ -17,7 +17,7 @@ program testpas2js;
 {$mode objfpc}{$H+}
 {$mode objfpc}{$H+}
 
 
 uses
 uses
-  Classes, consoletestrunner, tcconverter, fppas2js;
+  Classes, consoletestrunner, tcconverter, tcmodules;
 
 
 type
 type
 
 

+ 10 - 5
utils/fpdoc/dw_html.pp

@@ -1938,6 +1938,8 @@ var
   TREl, TDEl: TDOMElement;
   TREl, TDEl: TDOMElement;
   CurVariant: TPasVariant;
   CurVariant: TPasVariant;
   isExtended : Boolean;
   isExtended : Boolean;
+  VariantEl: TPasElement;
+  VariantType: TPasType;
 
 
 begin
 begin
   if not (Element.Parent is TPasVariant) then
   if not (Element.Parent is TPasVariant) then
@@ -1972,18 +1974,21 @@ begin
       AppendSym(CodeEl, ';');
       AppendSym(CodeEl, ';');
     end;
     end;
 
 
-  if Assigned(Element.VariantType) then
+  if Assigned(Element.VariantEl) then
   begin
   begin
     TREl := CreateTR(TableEl);
     TREl := CreateTR(TableEl);
     CodeEl := CreateCode(CreatePara(CreateTD_vtop(TREl)));
     CodeEl := CreateCode(CreatePara(CreateTD_vtop(TREl)));
     AppendNbSp(CodeEl, NestingLevel * 2 + 2);
     AppendNbSp(CodeEl, NestingLevel * 2 + 2);
     AppendKw(CodeEl, 'case ');
     AppendKw(CodeEl, 'case ');
-    if TPasRecordType(Element).VariantName <> '' then
+    VariantEl:=TPasRecordType(Element).VariantEl;
+    if VariantEl is TPasVariable then
     begin
     begin
-      AppendText(CodeEl, TPasRecordType(Element).VariantName);
+      AppendText(CodeEl, TPasVariable(VariantEl).Name);
       AppendSym(CodeEl, ': ');
       AppendSym(CodeEl, ': ');
-    end;
-    CodeEl := AppendType(CodeEl, TableEl, TPasRecordType(Element).VariantType, True);
+      VariantType:=TPasVariable(VariantEl).VarType;
+    end else
+      VariantType:=VariantEl as TPasType;
+    CodeEl := AppendType(CodeEl, TableEl, VariantType, True);
     AppendKw(CodeEl, ' of');
     AppendKw(CodeEl, ' of');
     for i := 0 to TPasRecordType(Element).Variants.Count - 1 do
     for i := 0 to TPasRecordType(Element).Variants.Count - 1 do
     begin
     begin