Przeglądaj źródła

* WIT document parser

Michaël Van Canneyt 1 miesiąc temu
rodzic
commit
ada0171abb

+ 2 - 0
packages/fcl-wit/Makefile

@@ -0,0 +1,2 @@
+PACKAGE_NAME=fcl-wit
+include ../build/Makefile.pkg

+ 8 - 0
packages/fcl-wit/README.md

@@ -0,0 +1,8 @@
+# FCL - WIT
+
+This directory contains the sources for a WIT document parser. 
+The WIT document describes WIT components, an interface definition language
+for webassembly modules.
+
+WIT is used in WasiP2, the second preview of the WASI system interface for
+webassembly.

+ 56 - 0
packages/fcl-wit/fpmake.pp

@@ -0,0 +1,56 @@
+{$ifndef ALLPACKAGES}
+{$mode objfpc}{$H+}
+program fpmake;
+
+uses {$ifdef unix}cthreads,{$endif} fpmkunit;
+
+Var
+  T : TTarget;
+  P : TPackage;
+begin
+  With Installer do
+    begin
+{$endif ALLPACKAGES}
+
+    P:=AddPackage('fcl-wit');
+    P.ShortName:='fclwit';
+{$ifdef ALLPACKAGES}
+    P.Directory:=ADirectory;
+{$endif ALLPACKAGES}
+    P.Version:='3.3.1';
+    P.Dependencies.Add('fcl-base');
+    P.Dependencies.Add('rtl-objpas');
+    P.Dependencies.Add('fcl-fpcunit');
+    P.Author := 'Michael van Canneyt';
+    P.License := 'LGPL with modification, ';
+    P.HomepageURL := 'www.freepascal.org';
+    P.Email := '';
+    P.Description := 'WIT document parser';
+    P.NeedLibC:= false;
+    P.OSes:=AllOSes;
+    if Defaults.CPU=jvm then
+      P.OSes := P.OSes - [java,android];
+
+    P.SourcePath.Add('src');
+
+    T:=P.Targets.AddUnit('wit.model.pp');
+    
+    T:=P.Targets.AddUnit('wit.scanner.pp');
+    T.ResourceStrings:=true;
+
+    T:=P.Targets.AddUnit('wit.parser.pp');
+    T.ResourceStrings:=true;
+    with T.Dependencies do
+      begin
+      AddUnit('wit.model');
+      AddUnit('wit.scanner');
+      end;
+      
+{$ifndef ALLPACKAGES}
+    Run;
+    end;
+end.
+{$endif ALLPACKAGES}
+
+
+

+ 1885 - 0
packages/fcl-wit/src/wit.model.pp

@@ -0,0 +1,1885 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2025 Michael Van Canneyt ([email protected])
+
+    classes that describe WIT document elements.
+
+    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.
+
+ **********************************************************************}
+
+unit WIT.Model;
+
+{$mode objfpc}
+{$H+}
+{$modeswitch advancedrecords}
+
+interface
+
+uses
+  {$IFDEF FPC_DOTTEDUNITS}
+  System.SysUtils, System.Classes, System.Contnrs;
+  {$ELSE}
+  sysutils, classes, contnrs;
+  {$ENDIF}
+
+type
+  EWIT = class(Exception)
+    errno : integer;
+  end;
+
+  TWITTypeKind = (
+    wtVoid, // Not an actual type, signifies none
+    wtIdentifier,
+    wtRecord,
+    wtFlags,
+    wtEnum,
+    wtResource,
+    wtTuple,
+    wtList,
+    wtOption,
+    wtResult,
+    wtVariant,
+    wtUnion,
+    wtString,
+    wtU8,
+    wtU16,
+    wtU32,
+    wtU64,
+    wtS8,
+    wtS16,
+    wtS32,
+    wtS64,
+    wtFloat32,
+    wtFloat64,
+    wtBool,
+    wtChar,
+    wtStream,
+    wtFuture,
+    wtFunction
+  );
+
+    { TGFPObjectList }
+
+  generic TGFPObjectList<T : TObject> = class (TFPObjectList)
+  private
+    Type
+
+       { TObjectEnum }
+
+       TObjectEnum = Class
+         FList : TFPObjectList;
+         FIdx : Integer;
+         constructor create(aList : TFPObjectList);
+         function GetCurrent : T;
+         function MoveNext: Boolean;
+         property Current : T read GetCurrent;
+       end;
+
+    function GetElement(aIndex: Integer): T;
+    procedure SetElement(aIndex: Integer; AValue: T);
+  Public
+    function getenumerator : TObjectEnum;
+    function add(aElement : T) : integer;
+    property Elements[aIndex: Integer] : T read GetElement Write SetElement; default;
+  end;
+
+  { TWITAnnotationArgument }
+
+  TWITAnnotationArgument = class (TObject)
+  private
+    FMember: string;
+    FValue: string;
+  public
+    constructor Create(const aMember, aValue: string);
+    destructor Destroy; override;
+    property Member: string read FMember;
+    property Value: string read FValue;
+  end;
+
+  TWITAnnotationArgumentList = Class(Specialize TGFPObjectList<TWITAnnotationArgument>);
+
+  TWITAnnotation = class (TObject)
+  private
+    FName: string;
+    FArguments: TWITAnnotationArgumentList;
+  public
+    constructor Create(const Name: string);
+    destructor Destroy; override;
+    property Name: string read FName write FName;
+    property Arguments: TWITAnnotationArgumentList read FArguments;
+  end;
+
+ TWITAnnotationList = Class(Specialize TGFPObjectList<TWITAnnotation>);
+
+  { TWITBaseElement }
+
+  TWITBaseElement = class (TObject)
+  private
+    FAnnotations: TWITAnnotationList;
+  Public
+    constructor create; virtual;
+    destructor destroy; override;
+    procedure AddAnnotation(aAnnotation : TWITAnnotation);
+    Property Annotations : TWITAnnotationList read FAnnotations;
+  end;
+
+  { TWITType }
+
+  TWITType = class (TWITBaseElement)
+  private
+    FKind: TWITTypeKind;
+  protected
+    // Will be called if IsNamed is true
+    function ToString(const aName : string) : string; virtual;
+    // Is this a named type: flags, enum, tuple, record, variant ?
+    class function IsNamed : Boolean; virtual;
+  public
+    constructor Create(Kind: TWITTypeKind); reintroduce;
+    function ToString: string; override;
+    property Kind: TWITTypeKind read FKind;
+  end;
+
+  { TWITNamedType }
+
+  TWITNamedType = class(TWITType)
+  private
+    FName: string;
+  public
+    constructor Create(const aName: string; aKind : TWITTypeKind); reintroduce;
+    property Name: string read FName;
+  end;
+
+  { TWITIdentifierType }
+
+  TWITIdentifierType = class(TWITNamedType)
+  Public
+    constructor Create(const aName : String);
+    function ToString: string; override;
+  end;
+
+  { TWITHandleType }
+
+  TWITHandleType = class(TWITIdentifierType)
+  private
+    FBorrowed: Boolean;
+  public
+    constructor Create(const aName : String; aBorrowed : Boolean = false);
+    function ToString: string; override;
+    property Borrowed : Boolean Read FBorrowed;
+  end;
+
+  { TWITTypeDef }
+
+  TWITTypeDef = class(TWITNamedType)
+  private
+    FTypeDef: TWitType;
+  Public
+    constructor Create(const aName: string; aTypeDef : TWitType);
+    destructor destroy; override;
+    function ToString : string; override;
+    property TypeDef : TWitType Read FTypeDef;
+  end;
+  TWITTypeDefList = specialize TGFPObjectList<TWITTypeDef>;
+
+
+  { TWITRecordField }
+
+  TWITRecordField = class(TWITBaseElement)
+  private
+    FName: string;
+    FType: TWITType;
+  public
+    Constructor Create(const aName : String; aType : TWITType);  reintroduce;
+    Destructor Destroy; override;
+    function ToString : string; override;
+    Property Name : string Read FName;
+    Property FieldType : TWITType Read FType;
+  end;
+
+  TWITRecordFieldList = class(Specialize TGFPObjectList<TWITRecordField>);
+
+  { TWITRecordType }
+
+  TWITRecordType = class(TWITType)
+  private
+    FFields: TWitRecordFieldList;
+  protected
+    function ToString(const aName: string): string; override;
+    class function IsNamed: Boolean; override;
+  public
+    constructor Create; reintroduce;
+    destructor Destroy; override;
+    procedure AddField(Field: TWITRecordField);
+    function ToString: string; override;
+    property Fields: TWITRecordFieldList read FFields;
+  end;
+
+
+
+  { TWITFlagsType }
+
+  TWITFlagsType = class(TWITType)
+  private
+    FFlags: TStrings;
+  protected
+    function ToString(const aName: string): string; override;
+    class function IsNamed: Boolean; override;
+  public
+    constructor Create; reintroduce;
+    destructor Destroy; override;
+    procedure AddFlag(const aFlag : string);
+    function ToString: string; override;
+    property Flags: TStrings read FFLags;
+  end;
+
+  { TWITEnumType }
+
+  TWITEnumType = class(TWITType)
+  private
+    FCases: TStrings;
+  protected
+    function ToString(const aName: string): string; override;
+    class function IsNamed: Boolean; override;
+  public
+    constructor Create;
+    destructor Destroy; override;
+    procedure AddCase(const aCase: String);
+    function ToString: string; override;
+    property Cases: TStrings read FCases;
+  end;
+
+  TWITTypeList = Class(Specialize TGFPObjectList<TWITType>);
+
+  { TWITTupleType }
+
+  TWITTupleType = class(TWITType)
+  private
+    FItems: TWITTypeList;
+  public
+    constructor Create;
+    destructor Destroy; override;
+    procedure AddItem(aItem: TWITType);
+    function ToString: string; override;
+    property Items: TWITTypeList read FItems;
+  end;
+
+
+  { TWITItemType }
+
+  TWITItemType = class(TWITType)
+  Private
+    FItemType: TWITType;
+  protected
+    class function MyKind : TWitTypeKind; virtual; abstract;
+    function EnvelopeName: String; virtual;
+    function ItemToString: String;
+  Public
+    constructor Create(aItemType: TWITType);
+    destructor destroy; override;
+    function ToString : string; override;
+    property ItemType: TWITType read FItemType;
+  end;
+
+    { TWITListType }
+
+  TWITListType = class(TWITItemType)
+  private
+    FItemCount: integer;
+  protected
+    class function MyKind : TWitTypeKind; override;
+  public
+    constructor Create(aItemType: TWITType; aItemCount : integer = 0);
+    function ToString: string; override;
+    property ItemCount : integer Read FItemCount Write FItemCount;
+  end;
+
+  { TWITOptionType }
+
+  TWITOptionType = class(TWITItemType)
+  protected
+    class function MyKind : TWitTypeKind; override;
+  end;
+
+  { TWITStreamType }
+
+  TWITStreamType = class(TWITItemType)
+  protected
+    class function MyKind : TWitTypeKind; override;
+  end;
+
+  { TWITStreamType }
+
+  { TWITFutureType }
+
+  TWITFutureType = class(TWITItemType)
+  protected
+    class function MyKind : TWitTypeKind; override;
+  end;
+
+
+  { TWITResultType }
+
+  TWITResultType = class(TWITType)
+  private
+    FOkType: TWITType;
+    FErrorType: TWITType;
+  public
+    constructor Create(aOkType: TWITType; aErrorType: TWITType); reintroduce;
+    destructor Destroy; override;
+    function ToString: string; override;
+    property OkType: TWITType read FOkType;
+    property ErrorType: TWITType read FErrorType;
+  end;
+
+  { TWITVariantCase }
+
+  TWITVariantCase = class (TObject)
+   private
+     FName: string;
+     FType: TWITType;
+   public
+     constructor Create(const aName: string; aOptionalType: TWITType = nil);
+     destructor destroy; override;
+     function ToString : string;
+     property Name: string read FName;
+     property OptionalType: TWITType read FType;
+   end;
+  TWITVariantCaseList = class(specialize TGFPObjectList<TWITVariantCase>);
+
+  { TWITVariantType }
+
+  TWITVariantType = class(TWITType)
+  private
+    FCases: TWITVariantCaseList;
+  protected
+    class function IsNamed: Boolean; override;
+    function ToString(const aName : string): string; override;
+  public
+    constructor Create();
+    destructor Destroy; override;
+    procedure AddCase(aCase: TWITVariantCase);
+    function ToString: string; override;
+    property Cases: TWITVariantCaseList read FCases;
+  end;
+
+  { TWITFuncParam }
+
+  TWITFuncParam = class(TWITBaseElement)
+  private
+    FName: string;
+    FType: TWITType;
+  public
+    constructor Create(const aName: string; aType : TWITType); reintroduce;
+    destructor Destroy; override;
+    function ToString : string; override;
+    property Name: string read FName write FName;
+    property ParamType: TWITType read FType write FType;
+  end;
+  TWITFuncParamList = class(specialize TGFPObjectList<TWITFuncParam>);
+
+  // Placeholder for your WIT function model (replace with your actual function type)
+
+  TWITFunctionFlag = (ffAsync, ffStatic, ffConstructor);
+  TWITFunctionFlags = set of TWITFunctionFlag;
+  { TWITFunctionType }
+
+  TWITFunctionType = class(TWITType)
+  private
+    FFlags: TWITFunctionFlags;
+    FParams : TWITFuncParamList;
+    FResultType: TWITType;
+    procedure SetResultType(AValue: TWITType);
+  Public
+    Constructor Create; Reintroduce;
+    destructor Destroy; override;
+    function ToString : String; override;
+    Property Parameters : TWITFuncParamList Read FParams;
+    Property Flags : TWITFunctionFlags Read FFlags Write FFlags;
+    Property ResultType: TWITType Read FResultType Write SetResultType;
+  end;
+
+  { TWITFunction }
+
+  TWITFunction = class(TWITBaseElement)
+  private
+    FName: string;
+    FTypeDef : TWITFunctionType;
+  public
+    constructor Create(const aName: string; aType : TWITFunctionType); reintroduce;
+    destructor Destroy; override;
+    function ToString : String; override;
+    property Name: string read FName;
+    property TypeDef : TWITFunctionType Read FTypeDef;
+  end;
+
+  TWITFunctionList = class(specialize TGFPObjectList<TWITFunction>);
+
+  { TWITResourceType }
+
+  TWITResourceType = class (TWITType)
+  private
+    FName: string;
+    FFunctions: TWITFunctionList;
+  public
+    constructor Create(const aName: string);
+    destructor Destroy; override;
+    function ToString : string; override;
+    property Name: string read FName write FName;
+    property Functions: TWITFunctionList read FFunctions;
+    procedure AddFunction(const aFunction: TWITFunction);
+  end;
+
+  { TWITUsePath }
+
+  TWITUsePath = class(TObject)
+  Private
+    FNamespaces: TStringList;
+    FPackageName: string;
+    FVersion: string;
+    FIdentifier: String;
+  Public
+    constructor Create; virtual;
+    destructor Destroy; override;
+    function ToString : string; override;
+    procedure AddNamespace(const aNamespace: string);
+    property Namespaces: TStringList read FNamespaces;
+    property PackageName: string read FPackageName write FPackageName;
+    property Version: string read FVersion write FVersion;
+    property Identifier: string read FIdentifier write FIdentifier;
+  end;
+
+  TWITBaseUse = class (TWITBaseElement)
+  private
+    FPath : TWITUsePath;
+    FRename: string;
+    procedure AddNamespace(const Namespace: string);
+  public
+    constructor Create;override;
+    destructor Destroy; override;
+    function ToString : string; override;
+    property Path : TWITUsePath read FPath;
+    property Rename: string read FRename write FRename;
+  end;
+
+  TWITTopLevelUse = Class(TWITBaseUse);
+  TWITTopLevelUsesList = class(specialize TGFPObjectList<TWITTopLevelUse>);
+
+  { TWitUseItem }
+
+  TWitUseItem = class(TWITBaseElement)
+  private
+    FAlias: string;
+    FName: string;
+  public
+    constructor Create(const aName : string; const aAlias : String = ''); reintroduce;
+    function ToString : string; override;
+    property Name : string Read FName;
+    property Alias : string Read FAlias;
+  end;
+  TWITUseItemList = class(specialize TGFPObjectList<TWITUseItem>);
+
+  { TWITUse }
+
+  TWITUse = class (TWITBaseUse)
+  Private
+    FItems : TWITUseItemList;
+    FUses: TWITUseItemList;
+  public
+    constructor create; override;
+    destructor destroy; override;
+    function ToString : string; override;
+    procedure AddItem(aItem : TWITUseItem);
+    procedure AddItem(const aName : string; aAlias : string = '');
+    Property Items : TWITUseItemList Read FUses;
+  end;
+  TWITInterfaceUseList = class(specialize TGFPObjectList<TWITUse>);
+
+
+  { TWITInterface }
+
+  TWITInterface = class (TWITBaseElement)
+  private
+    FName: string;
+    FFunctions: TWITFunctionList;
+    FTypes: TWITTypeDefList;
+    FUseList : TWITInterfaceUseList;
+  public
+    constructor Create(const aName: string); reintroduce;
+    destructor Destroy; override;
+    procedure AddFunction(aFunction: TWITFunction);
+    procedure AddType(aType: TWITTypeDef);
+    procedure AddUses(aUse: TWITUse);
+    function ToString: String override;
+    property Name: string read FName write FName;
+    property Functions: TWITFunctionList read FFunctions;
+    property Types: TWITTypeDefList read FTypes;
+    property UseList : TWITInterfaceUseList Read FUseList;
+  end;
+  TWITInterfaceList = class(specialize TGFPObjectList<TWITInterface>);
+
+
+
+  TExchangeType = (xtImport,xtExport);
+
+  { TWITExchange }
+
+  TWITExchange = class (TWITBaseElement)
+  private
+    FName: string;
+    FType: TExChangeType;
+  public
+    constructor Create(aType : TExchangeType; const aName: string); reintroduce;
+    property Name: string read FName write FName;
+    Property ExtrangeType : TExChangeType Read FType;
+  end;
+  TWITImportList = class(specialize TGFPObjectList<TWITExchange>);
+  TWITExportList = class(specialize TGFPObjectList<TWITExchange>);
+
+  { TWITExportIdentifier }
+
+  { TWITExchangeIdentifier }
+  TWITExchangeIdentifier = class (TWITExchange)
+  private
+    FUse: TWITUsePath;
+  public
+    constructor Create(aType : TExchangeType; aPath : string);
+    constructor Create(aType : TExchangeType; aUse: TWITUsePath);
+    destructor destroy; override;
+    function ToString : string; override;
+    property UseDef : TWITUsePath read FUse;
+  end;
+
+  { TWITExportFunc }
+
+  TWITExchangeFunc = class (TWITExchange)
+  private
+    FTypeDef: TWITFunctionType;
+  public
+    constructor Create(aType : TExchangeType; const aName: string; aFunc : TWITFunctionType);
+    destructor Destroy; override;
+    property TypeDef : TWITFunctionType read FTypeDef;
+  end;
+
+    { TWITExportFunc }
+
+  { TWITExportInterface }
+
+  { TWITExchangeInterface }
+
+  TWITExchangeInterface = class (TWITExchange)
+  private
+    FTypeDef: TWITInterface;
+  public
+    constructor Create(aType : TExchangeType; const aName: string; aIntf : TWITInterface);
+    destructor Destroy; override;
+    property TypeDef : TWITInterface read FTypeDef;
+  end;
+
+
+
+  { TWitIncludeItem }
+
+  TWitIncludeItem = class(TWITBaseElement)
+  private
+    FAlias: string;
+    FName: string;
+  public
+    constructor Create(const aName : string; const aAlias : String = ''); reintroduce;
+    function ToString : string; override;
+    property Name : string Read FName;
+    property Alias : string Read FAlias;
+  end;
+  TWITIncludeItemList = class(specialize TGFPObjectList<TWITIncludeItem>);
+
+  TWITInclude = class(TWITBaseElement)
+  private
+    FItems: TWitIncludeItemList;
+    FPath: TWITUsePath;
+  Public
+    Constructor create; override;
+    destructor destroy; override;
+    function ToString : string; override;
+    function AddItem(const aItem : String; aAlias : String = '') : TWitIncludeItem;
+    Property Path : TWITUsePath Read FPath;
+    Property Items : TWitIncludeItemList Read FItems;
+  end;
+  TWITIncludeList = class(specialize TGFPObjectList<TWITInclude>);
+
+  { TWITWorld }
+
+  TWITWorld = class (TWITBaseElement)
+  private
+    FIncludes: TWITIncludeList;
+    FName: string;
+    FImports: TWITImportList;
+    FExports: TWITExportList;
+    FTypeDefs: TWITTypeDefList;
+    FUses: TWITInterfaceUseList;
+  public
+    constructor Create(const Name: string); reintroduce;
+    destructor Destroy; override;
+    function ToString : string; override;
+    procedure AddImport(Import: TWITExchange);
+    procedure AddExport(Export: TWITExchange);
+    procedure AddInclude(aInclude: TWITInclude);
+    procedure AddUses(aUses: TWITUse);
+    procedure AddTypeDef(aTypeDef: TWITTypeDef);
+    property Name: string read FName write FName;
+    property Imported: TWITImportList read FImports;
+    property Exported: TWITExportList read FExports;
+    property TypeDefs: TWITTypeDefList read FTypeDefs;
+    property UsesList: TWITInterfaceUseList read FUses;
+    property Includes: TWITIncludeList read FIncludes;
+  end;
+  TWITWorldList = class(specialize TGFPObjectList<TWITWorld>);
+
+  { TWITPackage }
+
+  TWITPackage = class (TWITBaseELement)
+  private
+    FIsNested: Boolean;
+    FNamespace: string;
+    FPackageName: string;
+    FVersion: string;
+    FImports: TStringList;
+    FExports: TStringList;
+    FUseStatements: TWitTopLevelUsesList;
+    FInterfaces: TWITInterfaceList;
+    FWorlds: TWITWorldList;
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+    function ToString : string; override;
+    property Namespace: string read FNamespace write FNamespace;
+    property PackageName: string read FPackageName write FPackageName;
+    property Version: string read FVersion write FVersion;
+    property ImportList: TStringList read FImports;
+    property ExportList: TStringList read FExports;
+    property UseStatements: TWitTopLevelUsesList read FUseStatements;
+    property Interfaces: TWITInterfaceList read FInterfaces;
+    property Worlds: TWITWorldList read FWorlds;
+    Property IsNested : Boolean Read FIsNested Write FIsNested;
+  end;
+  TWITPackageList = class(specialize TGFPObjectList<TWITPackage>);
+
+
+
+  { TWITDocument }
+
+  TWITDocument = class  (TWITBaseElement)
+  private
+    FDefaultPackage: TWitPackage;
+    FPackages: TWITPackageList;
+    function GetDefaultPackage: TWITPackage;
+    function GetInterfaces: TWITInterfaceList;
+    function GetUseStatements: TWITTopLevelUsesList;
+    function GetWorlds: TWITWorldList;
+    procedure SetDefaultPackage(const aValue: TWITPackage);
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+    property DefaultPackage : TWITPackage Read GetDefaultPackage Write SetDefaultPackage;
+    property Packages: TWITPackageList read FPackages;
+    property Interfaces: TWITInterfaceList read GetInterfaces;
+    property UseStatements: TWITTopLevelUsesList read GetUseStatements;
+    property Worlds: TWITWorldList read GetWorlds;
+    procedure AddInterface(aInterface: TWITInterface);
+    procedure AddUse(aUse: TWITTopLevelUse);
+    procedure AddWorld(aWorld: TWITWorld);
+  end;
+
+implementation
+
+
+{ TWITType }
+
+function TWITType.ToString(const aName: string): string;
+begin
+  Result:=ToString()+' '+aname+';';
+end;
+
+class function TWITType.IsNamed: Boolean;
+begin
+  Result:=False;
+end;
+
+constructor TWITType.Create(Kind: TWITTypeKind);
+begin
+  inherited create;
+  FKind := Kind;
+end;
+
+function TWITType.ToString: string;
+begin
+  WriteStr(Result,fkind);
+  Delete(Result,1,2);
+  Result:=LowerCase(Result);
+end;
+
+{ TWITNamedType }
+
+constructor TWITNamedType.Create(const aName: string; aKind: TWITTypeKind);
+begin
+  inherited Create(Akind);
+  FName := aName;
+end;
+
+{ TWITIdentifierType }
+
+constructor TWITIdentifierType.Create(const aName: String);
+begin
+  Inherited Create(aName,wtIdentifier);
+end;
+
+function TWITIdentifierType.ToString: string;
+begin
+  Result := FName;
+end;
+
+{ TWITHandleType }
+
+constructor TWITHandleType.Create(const aName: String; aBorrowed: Boolean);
+begin
+  Inherited create(aName);
+  FBorrowed:=aBorrowed;
+end;
+
+function TWITHandleType.ToString: string;
+begin
+  Result:=inherited ToString;
+  if FBorrowed then
+    Result:='borrow<'+Result+'>'
+  else
+    Result:='own<'+Result+'>';
+end;
+
+{ TWITNamedType }
+
+constructor TWITTypeDef.Create(const aName: string; aTypeDef: TWitType);
+begin
+  Inherited Create(aName,aTypeDef.Kind);
+  FTypeDef:=aTypeDef;
+end;
+
+destructor TWITTypeDef.destroy;
+begin
+  FreeAndNil(FTypeDef);
+  inherited destroy;
+end;
+
+function TWITTypeDef.ToString: string;
+
+begin
+  if TypeDef.IsNamed then
+    Result:=TypeDef.ToString(Name)
+  else
+    Result:='type '+Name+' = '+TypeDef.ToString+';'
+end;
+
+{ TWITRecordField }
+
+constructor TWITRecordField.Create(const aName: String; aType: TWITType);
+begin
+  Inherited Create;
+  FName:=aName;
+  FType:=aType;
+end;
+
+destructor TWITRecordField.Destroy;
+begin
+  FreeAndNil(FType);
+  inherited Destroy;
+end;
+
+function TWITRecordField.ToString: string;
+begin
+  Result:=name+' : '+FieldType.ToString;
+end;
+
+{ TGFPObjectList }
+
+function TGFPObjectList.GetElement(aIndex: Integer): T;
+begin
+  Result:=T(Items[aIndex]);
+end;
+
+procedure TGFPObjectList.SetElement(aIndex: Integer; AValue: T);
+begin
+  Items[aIndex]:=aValue;
+end;
+
+function TGFPObjectList.getenumerator: TObjectEnum;
+begin
+  Result:=TObjectEnum.Create(Self);
+
+end;
+
+function TGFPObjectList.add(aElement: T): integer;
+begin
+  Result:=Inherited add(aElement);
+end;
+
+{ TGFPObjectList.TObjectEnum }
+
+constructor TGFPObjectList.TObjectEnum.create(aList: TFPObjectList);
+begin
+  FList:=aList;
+  FIdx:=-1;
+end;
+
+function TGFPObjectList.TObjectEnum.GetCurrent: T;
+begin
+  If FIdx<0 then
+    result:=Nil
+  else
+    Result:=T(FList[FIdx]);
+end;
+
+function TGFPObjectList.TObjectEnum.MoveNext: Boolean;
+begin
+  Inc(FIdx);
+  Result:=FIdx<FList.Count;
+
+end;
+
+{ TWITAnnotationArgument }
+
+constructor TWITAnnotationArgument.Create(const aMember, aValue: string);
+begin
+  FMember:=aMember;
+  FValue:=aValue;
+end;
+
+destructor TWITAnnotationArgument.Destroy;
+begin
+  inherited Destroy;
+end;
+
+{ TWITRecordType }
+
+function TWITRecordType.ToString(const aName: string): string;
+
+var
+  Field : TWITRecordField;
+begin
+  Result:='';
+  for Field in FFields do
+  begin
+    if Result<>'' then
+      Result:=Result+','+sLineBreak;
+    Result := Result + '  '+Field.ToString ;
+  end;
+  if Result<>'' then
+    Result:=sLineBreak+Result+sLineBreak;
+  Result := 'record '+aName+' {'+Result+'}';
+
+end;
+
+class function TWITRecordType.IsNamed: Boolean;
+begin
+  Result:=True;
+end;
+
+constructor TWITRecordType.Create;
+begin
+  inherited Create(wtRecord);
+  FFields := TWITRecordFieldList.Create;
+end;
+
+destructor TWITRecordType.Destroy;
+begin
+  FFields.Free;
+  inherited Destroy;
+end;
+
+procedure TWITRecordType.AddField(Field: TWITRecordField);
+begin
+  FFields.Add(Field);
+end;
+
+function TWITRecordType.ToString: string;
+
+begin
+  Result:=ToString('');
+end;
+
+{ TWITFlagsType }
+
+function TWITFlagsType.ToString(const aName: string): string;
+var
+  I : integer;
+begin
+  Result:='';
+  for I:=0 to Flags.Count-1 do
+    begin
+    if I>0 then
+      Result:=Result+','+sLineBreak;
+    Result:=Result+'  '+Flags[i];
+    end;
+  if Result<>'' then
+    Result:=sLinebreak+Result+sLinebreak;
+  Result:='flags '+aName+' {'+Result+'}';
+end;
+
+class function TWITFlagsType.IsNamed: Boolean;
+begin
+  Result:=True;
+end;
+
+constructor TWITFlagsType.Create;
+begin
+  Inherited Create(wtFlags);
+  FFlags:=TStringList.Create;
+end;
+
+destructor TWITFlagsType.Destroy;
+begin
+  FreeAndNil(FFlags);
+  inherited Destroy;
+end;
+
+procedure TWITFlagsType.AddFlag(const aFlag: string);
+begin
+  FFlags.Add(aFlag);
+end;
+
+function TWITFlagsType.ToString: string;
+begin
+  Result:=ToString('');
+end;
+
+{ TWITEnumType }
+
+function TWITEnumType.ToString(const aName: string): string;
+var
+  I : integer;
+begin
+  Result:='';
+  for I:=0 to FCases.Count-1 do
+    begin
+    if I>0 then
+      Result:=Result+','+sLineBreak;
+    Result:=Result+'  '+FCases[i]
+    end;
+  if Result<>'' then
+    Result:=sLinebreak+Result+sLinebreak;
+  Result:='enum '+aName+' {'+Result+'}';
+end;
+
+class function TWITEnumType.IsNamed: Boolean;
+begin
+  Result:=true;
+end;
+
+constructor TWITEnumType.Create;
+begin
+  Inherited create(wtEnum);
+  FCases:=TStringList.Create;
+end;
+
+destructor TWITEnumType.Destroy;
+begin
+  FreeAndNil(FCases);
+  inherited Destroy;
+end;
+
+procedure TWITEnumType.AddCase(const aCase: String);
+begin
+  FCases.Add(aCase);
+end;
+
+function TWITEnumType.ToString: string;
+begin
+  Result:=ToString('');
+end;
+
+{ TWITTupleType }
+
+constructor TWITTupleType.Create;
+begin
+  Inherited create(wtTuple);
+  FItems:=TWITTypelist.Create(True);
+end;
+
+destructor TWITTupleType.Destroy;
+begin
+  FreeAndNil(FItems);
+  inherited Destroy;
+end;
+
+procedure TWITTupleType.AddItem(aItem: TWITType);
+begin
+  FItems.Add(aItem);
+end;
+
+function TWITTupleType.ToString: string;
+var
+  i : integer;
+begin
+  Result:='';
+  for I:=0 to Items.Count-1 do
+    begin
+    if I>0 then
+      Result:=Result+',';
+    Result:=Result+Items[i].ToString;
+    end;
+  Result:='tuple<'+Result+'>';
+end;
+
+{ TWITItemType }
+
+function TWITItemType.EnvelopeName: String;
+begin
+  WriteStr(Result,Kind);
+  Result:=LowerCase(Copy(Result,3,Length(Result)-2));
+end;
+
+function TWITItemType.ItemToString: String;
+begin
+  Result:='';
+  if Assigned(ItemType) then
+    Result:=ItemType.ToString;
+end;
+
+constructor TWITItemType.Create(aItemType: TWITType);
+begin
+  Inherited Create(MyKind);
+  FItemType:=aItemType;
+end;
+
+destructor TWITItemType.destroy;
+begin
+  FreeAndNil(FItemType);
+  inherited destroy;
+end;
+
+function TWITItemType.ToString: string;
+
+begin
+  Result:=EnvelopeName+'<'+ItemToString+'>';
+end;
+
+{ TWITListType }
+
+class function TWITListType.MyKind: TWitTypeKind;
+begin
+  Result:=wtList;
+end;
+
+constructor TWITListType.Create(aItemType: TWITType; aItemCount: integer);
+begin
+  Inherited create(aItemType);
+  FItemCount:=aItemCount;
+end;
+
+function TWITListType.ToString: string;
+
+begin
+  Result:=EnvelopeName+'<'+ItemToString;
+  if ItemCount>0 then
+    Result:=Result+','+IntTostr(ItemCount);
+  Result:=Result+'>';
+end;
+
+{ TWITOptionType }
+
+class function TWITOptionType.MyKind: TWitTypeKind;
+begin
+  Result:=wtOption;
+end;
+
+
+{ TWITStreamType }
+
+class function TWITStreamType.MyKind: TWitTypeKind;
+begin
+  Result:=wtStream;
+end;
+
+
+{ TWITFutureType }
+
+class function TWITFutureType.MyKind: TWitTypeKind;
+begin
+  Result:=wtFuture;
+end;
+
+{ TWITResultType }
+
+constructor TWITResultType.Create(aOkType: TWITType; aErrorType: TWITType);
+begin
+  inherited create(wtResult);
+  FOkType:=aOkType;
+  FErrorType:=aErrorType;
+end;
+
+destructor TWITResultType.Destroy;
+begin
+  FreeAndNil(FOkType);
+  FreeAndNil(FErrorType);
+  inherited Destroy;
+end;
+
+function TWITResultType.ToString: string;
+begin
+  Result:='';
+  if Assigned(OkType) then
+    result:=OKType.ToString;
+  if Assigned(ErrorType) then
+    begin
+    if Result='' then
+      Result:='_';
+    Result:=Result+','+ErrorType.ToString;
+    end;
+  if Result<>'' then
+    Result:='<'+result+'>';
+  Result:='result'+Result;
+end;
+
+{ TWITVariantCase }
+
+constructor TWITVariantCase.Create(const aName: string; aOptionalType: TWITType);
+begin
+  FName:=aName;
+  FType:=aOptionalType;
+end;
+
+destructor TWITVariantCase.destroy;
+begin
+  FreeAndNil(FType);
+  inherited destroy;
+end;
+
+function TWITVariantCase.ToString: string;
+begin
+  Result:=Name;
+  if Assigned(OptionalType) then
+    Result:= Result+'('+OptionalType.ToString+')';
+end;
+
+{ TWITVariantType }
+
+class function TWITVariantType.IsNamed: Boolean;
+begin
+  Result:=True
+end;
+
+function TWITVariantType.ToString(const aName: string): string;
+var
+  I : integer;
+begin
+  Result:='';
+  for I:=0 to Cases.Count-1 do
+    begin
+    if Result<>'' then
+      Result:=Result+','+sLinebreak;
+    Result:=Result+'  '+Cases[I].ToString;
+    end;
+  if Result<>'' then
+    Result:=sLineBreak+Result+sLineBreak;
+  Result:='variant '+aName+' {'+Result+'}';
+end;
+
+constructor TWITVariantType.Create;
+begin
+  inherited create(wtVariant);
+  FCases:=TWITVariantCaseList.Create(True);
+end;
+
+destructor TWITVariantType.Destroy;
+begin
+  FreeAndNil(FCases);
+  inherited Destroy;
+end;
+
+procedure TWITVariantType.AddCase(aCase: TWITVariantCase);
+begin
+  FCases.Add(aCase);
+end;
+
+function TWITVariantType.ToString: string;
+begin
+  Result:=tostring('');
+end;
+
+{ TWITFuncParam }
+
+constructor TWITFuncParam.Create(const aName: string; aType: TWITType);
+begin
+  FName:=aName;
+  FType:=aType;
+end;
+
+destructor TWITFuncParam.Destroy;
+begin
+  FreeAndNil(FType);
+  inherited Destroy;
+end;
+
+function TWITFuncParam.ToString: string;
+begin
+  Result:=Name+': '+ParamType.ToString;
+end;
+
+{ TWITFunction }
+
+constructor TWITFunctionType.Create();
+begin
+  Inherited Create(wtFunction);
+  FParams:=TWITFuncParamList.Create(True);
+end;
+
+procedure TWITFunctionType.SetResultType(AValue: TWITType);
+begin
+  if FResultType=AValue then Exit;
+  FreeAndNil(FResultType);
+  FResultType:=AValue;
+end;
+
+destructor TWITFunctionType.Destroy;
+begin
+  FreeAndNil(FParams);
+  FreeAndNil(FResultType);
+  inherited Destroy;
+end;
+
+function TWITFunctionType.ToString: String;
+var
+  LParam : TWITFuncParam;
+begin
+  Result:='';
+  for lParam in Parameters do
+    begin
+    if Result<>'' then
+      Result:=Result+', ';
+    Result:=Result+lParam.ToString;
+    end;
+  Result:='('+Result+')';
+  if ResultType<>nil then
+    Result:=Result+' -> '+ResultType.ToString;
+  if ffConstructor in FFlags then
+    Result:='constructor'+Result
+  else
+    Result:='func'+Result;
+  if ffAsync in FFlags then
+    Result:='async '+Result
+  else if ffStatic in FFlags then
+    Result:='static '+Result;
+end;
+
+constructor TWITFunction.Create(const aName: string; aType: TWITFunctionType);
+begin
+  Inherited create;
+  FName:=aName;
+  FTypeDef:=aType;
+end;
+
+destructor TWITFunction.Destroy;
+begin
+  FreeAndNil(FTypeDef);
+  Inherited;
+end;
+
+function TWITFunction.ToString: String;
+begin
+  Result:=TypeDef.ToString;
+  if not (ffConstructor in TypeDef.Flags) then
+    Result:=Name+' : '+Result;
+  Result:=Result+';';
+end;
+
+
+{ TWITResourceType }
+
+constructor TWITResourceType.Create(const aName: string);
+begin
+  inherited create(wtResource);
+  FName := aName;
+  FFunctions := TWITFunctionList.Create; // Initialize functions list
+end;
+
+destructor TWITResourceType.Destroy;
+begin
+  FFunctions.Free;
+  inherited Destroy;
+end;
+
+function TWITResourceType.ToString: string;
+var
+  I : Integer;
+begin
+  Result:='';
+  for I:=0 to Functions.Count-1 do
+    begin
+    if Result<>'' then
+      Result:=Result+sLineBreak;
+    Result:=Result+'  '+Functions[i].ToString;
+    end;
+  if Result<>'' then
+    Result:=' {'+sLineBreak+Result+sLineBreak+'}'
+  else
+    Result:=';';
+  Result:='resource '+Name+Result;
+end;
+
+procedure TWITResourceType.AddFunction(const aFunction: TWITFunction);
+begin
+  FFunctions.Add(aFunction);
+end;
+
+{ TWITPackage }
+
+constructor TWITPackage.Create;
+begin
+  Inherited Create;
+  FImports := TStringList.Create;
+  FExports := TStringList.Create;
+  FWorlds:=TWITWorldList.Create;
+  FUseStatements := TWITTopLevelUsesList.Create;
+  FInterfaces := TWITInterfaceList.Create;
+end;
+
+destructor TWITPackage.Destroy;
+begin
+  FreeAndNil(FImports);
+  FreeAndNil(FExports);
+  FreeAndNil(FUseStatements);
+  FreeAndNil(FInterfaces);
+  FreeAndNil(FWorlds);
+  inherited Destroy;
+end;
+
+function TWITPackage.ToString: string;
+var
+  i : integer;
+begin
+  Result:=PackageName;
+  if Namespace<>'' then
+    Result:=Namespace+':'+Result;
+  if Version<>'' then
+    Result:=Result+'@'+Version;
+
+  Result:='package '+ Result;
+  if not isNested then
+    begin
+    Result:=Result+';';
+    if (UseStatements.Count>0) or (Worlds.Count>0) or (Interfaces.Count>0) then
+      Result:=Result+sLineBreak;
+    end
+  else
+    Result:=Result+' {'+sLineBreak;
+  for I:=0 to UseStatements.Count-1 do
+     Result:=Result+UseStatements[i].ToString+sLineBreak;
+  for I:=0 to Worlds.Count-1 do
+     Result:=Result+worlds[i].ToString+sLineBreak;
+  for I:=0 to Interfaces.Count-1 do
+     Result:=Result+Interfaces[i].ToString+sLineBreak;
+  if isNested then
+    Result:=Result+'}'+sLineBreak;
+end;
+
+{ TWITAnnotation }
+
+constructor TWITAnnotation.Create(const Name: string);
+begin
+  FName := Name;
+  FArguments := TWITAnnotationArgumentList.Create(true);
+end;
+
+destructor TWITAnnotation.Destroy;
+begin
+  FArguments.Free;
+  inherited Destroy;
+end;
+
+{ TWITBaseElement }
+
+constructor TWITBaseElement.create;
+begin
+  FAnnotations:=TWITAnnotationList.Create(True);
+end;
+
+destructor TWITBaseElement.destroy;
+begin
+  FAnnotations.Free;
+  Inherited;
+end;
+
+procedure TWITBaseElement.AddAnnotation(aAnnotation: TWITAnnotation);
+begin
+  FAnnotations.Add(aAnnotation);
+end;
+
+{ TWITInterface }
+
+constructor TWITInterface.Create(const aName: string);
+begin
+  inherited create;
+  FName := aName;
+  FFunctions := TWITFunctionList.Create;
+  FTypes:= TWITTypeDefList.Create;
+  FUseList:=TWITInterfaceUseList.Create;
+end;
+
+destructor TWITInterface.Destroy;
+begin
+  FreeAndNil(FUseList);
+  FreeAndNil(FFunctions);
+  FreeAndNil(FTypes);
+  inherited Destroy;
+end;
+
+procedure TWITInterface.AddFunction(aFunction: TWITFunction);
+begin
+  FFunctions.Add(aFunction);
+end;
+
+procedure TWITInterface.AddType(aType: TWITTypeDef);
+begin
+  FTypes.Add(aType);
+end;
+
+procedure TWITInterface.AddUses(aUse: TWITUse);
+begin
+  FUseList.Add(aUse);
+end;
+
+function TWITInterface.ToString: String;
+
+  procedure AddToResult(aDef : String);
+  begin
+    if Result<>'' then
+      Result:=Result+sLineBreak;
+    Result:=Result+'  '+aDef;
+  end;
+
+var
+  LType : TWITTypeDef;
+  lFunc : TWITFunction;
+  LUse : TWITUse;
+
+begin
+  Result:='';
+  For LUse in UseList do
+    AddToResult(LUse.ToString);
+  For LType in Types do
+    AddToResult(lType.ToString);
+  For lFunc in Functions do
+    AddToResult(lFunc.ToString);
+  if Result<>'' then
+    Result:=sLineBreak+Result+sLineBreak;
+  Result:='interface '+Name+' {'+Result+'}';
+end;
+
+{ TWITUsePath }
+
+constructor TWITUsePath.Create;
+begin
+  FNamespaces:=TStringList.Create;
+end;
+
+destructor TWITUsePath.Destroy;
+begin
+  FreeAndNil(FNameSpaces);
+  Inherited;
+end;
+
+function TWITUsePath.ToString: string;
+var
+  i : Integer;
+begin
+  Result:='';
+  For I:=0 to Namespaces.Count-1 do
+    begin
+    if I>0 then
+      Result:=Result+':';
+    Result:=Result+NameSpaces[i]
+    end;
+  if PackageName<>'' then
+    begin
+    if Result<>'' then
+      Result:=Result+':';
+    Result:=Result+PackageName;
+    end;
+  if Identifier<>'' then
+    begin
+    if Result<>'' then
+      Result:=Result+'/';
+    Result:=Result+Identifier;
+    end;
+  if Version<>'' then
+    Result:=Result+'@'+Version;
+end;
+
+procedure TWITUsePath.AddNamespace(const aNamespace: string);
+begin
+  FNamespaces.Add(aNameSpace);
+end;
+
+
+{ TWITDocument }
+
+function TWITDocument.GetInterfaces: TWITInterfaceList;
+begin
+  Result:=DefaultPackage.Interfaces;
+end;
+
+function TWITDocument.GetUseStatements: TWITTopLevelUsesList;
+begin
+  Result:=DefaultPackage.UseStatements;
+end;
+
+function TWITDocument.GetDefaultPackage: TWITPackage;
+begin
+  if FDefaultPackage=Nil then
+    begin
+    FDefaultPackage:=TWITPackage.Create;
+    FPackages.Add(FDefaultPackage);
+    end;
+  Result:=FDefaultPackage;
+end;
+
+function TWITDocument.GetWorlds: TWITWorldList;
+begin
+  Result:=DefaultPackage.Worlds;
+end;
+
+procedure TWITDocument.SetDefaultPackage(const aValue: TWITPackage);
+begin
+  if FDefaultPackage<>Nil then
+    Raise EWIT.Create('Default package already set.');
+  if aValue.IsNested then
+    Raise EWIT.Create('Default package cannot be nested.');
+  FDefaultPackage:=aValue;
+end;
+
+constructor TWITDocument.Create;
+begin
+  Inherited create;
+  FPackages := TWITPackageList.Create(True);
+end;
+
+destructor TWITDocument.Destroy;
+
+begin
+  FPackages.Free;
+  inherited Destroy;
+end;
+
+procedure TWITDocument.AddInterface(aInterface: TWITInterface);
+begin
+  DefaultPackage.Interfaces.Add(aInterface);
+end;
+
+procedure TWITDocument.AddUse(aUse: TWITTopLevelUse);
+begin
+  DefaultPackage.UseStatements.Add(aUse);
+end;
+
+procedure TWITDocument.AddWorld(aWorld : TWITWorld);
+begin
+  DefaultPackage.Worlds.Add(aWorld);
+end;
+
+
+
+{ TWITExchange }
+
+constructor TWITExchange.Create(aType: TExchangeType; const aName: string);
+begin
+  inherited create;
+  FName := aName;
+  FType:=aType;
+end;
+
+{ TWITExportIdentifier }
+
+constructor TWITExchangeIdentifier.Create(aType: TExchangeType; aPath: string);
+begin
+  Inherited Create(aType,aPath);
+  FUse:=TWITUsePath.Create;
+  FUse.Identifier:=aPath;
+end;
+
+constructor TWITExchangeIdentifier.Create(aType: TExchangeType; aUse: TWITUsePath);
+begin
+  Inherited Create(aType,aUse.ToString);
+  FUse:=aUse;
+  FType:=aType;
+end;
+
+
+destructor TWITExchangeIdentifier.destroy;
+begin
+  FreeAndNil(FUse);
+  inherited destroy;
+end;
+
+function TWITExchangeIdentifier.ToString: string;
+
+begin
+  Result:=UseDef.ToString+';';
+  if FType=xtImport then
+    Result:='import '+Result
+  else
+    Result:='export '+Result
+end;
+
+{ TWITExportFunc }
+
+constructor TWITExchangeFunc.Create(aType : TExchangeType; const aName: string; aFunc: TWITFunctionType);
+begin
+  inherited create(aType,aName);
+  FTypeDef:=aFunc;
+end;
+
+destructor TWITExchangeFunc.Destroy;
+begin
+  FreeAndNil(FTypeDef);
+  inherited Destroy;
+end;
+
+{ TWITExportInterface }
+
+constructor TWITExchangeInterface.Create(aType: TExchangeType; const aName: string; aIntf: TWITInterface);
+begin
+  Inherited create(aType,aName);
+  FTypeDef:=aIntf;
+end;
+
+destructor TWITExchangeInterface.Destroy;
+begin
+  FreeAndNil(FTypeDef);
+  inherited Destroy;
+end;
+
+{ TWITWorld }
+
+constructor TWITWorld.Create(const Name: string);
+begin
+  Inherited Create;
+  FName := Name;
+  FImports := TWITImportList.Create(True);
+  FExports := TWITExportList.Create(True);
+//  FInterfaces := TWITInterfaceList.Create(True);
+  FTypeDefs := TWITTypeDefList.Create(True);
+  FUses := TWITInterfaceUseList.Create(True);
+  FIncludes := TWITIncludeList.Create(True);
+end;
+
+destructor TWITWorld.Destroy;
+begin
+  FreeAndNil(FIncludes);
+  FreeAndNil(FTypeDefs);
+//  FreeAndNil(FInterfaces);
+  FreeAndNil(FImports);
+  FreeAndNil(FExports);
+  FreeAndNil(FUses);
+  inherited Destroy;
+end;
+
+function TWITWorld.ToString: string;
+
+  procedure AddToResult(aLine : string);
+
+  begin
+    if Result<>'' then
+      Result:=Result+sLineBreak;
+    Result:=Result+'  '+aLine;
+  end;
+
+var
+  lEx : TWITExchange;
+  lUse : TWITBaseUse;
+  lType : TWITTypeDef;
+  lInclude : TWITInclude;
+
+
+begin
+  Result:='';
+  For lInclude in Includes do
+    AddToResult(lInclude.ToString);
+  For lUse in UsesList do
+    AddToResult(lUse.ToString);
+  for lType in TypeDefs do
+    AddToResult(lType.ToString);
+  For lEx in Imported do
+    AddToResult(lEx.ToString);
+  For lEx in Exported do
+    AddToResult(lEx.ToString);
+
+  if Result<>'' then
+    Result:=sLineBreak+Result+sLineBreak;
+  Result:='world '+Name+' {'+Result+'}';
+end;
+
+procedure TWITWorld.AddImport(Import: TWITExchange);
+begin
+  FImports.Add(Import);
+end;
+
+procedure TWITWorld.AddExport(Export: TWITExchange);
+begin
+  FExports.Add(Export);
+end;
+
+procedure TWITWorld.AddInclude(aInclude: TWITInclude);
+begin
+  FIncludes.add(aInclude);
+end;
+
+
+procedure TWITWorld.AddUses(aUses: TWITUse);
+begin
+  FUses.add(aUses);
+end;
+
+procedure TWITWorld.AddTypeDef(aTypeDef: TWITTypeDef);
+begin
+  FTypeDefs.Add(aTypeDef);
+end;
+
+
+{ TWITBaseUse }
+
+constructor TWITBaseUse.Create;
+begin
+  Inherited;
+  FPath:=TWITUsePath.Create;
+  FRename := '';
+end;
+
+destructor TWITBaseUse.Destroy;
+begin
+  FPath.Free;
+  inherited Destroy;
+end;
+
+function TWITBaseUse.ToString: string;
+begin
+  Result:=FPath.ToString;
+  if Rename<>'' then
+    Result:=Result+' as '+Rename;
+  Result:='use '+Result+';';
+end;
+
+procedure TWITBaseUse.AddNamespace(const Namespace: string);
+begin
+  FPath.AddNamespace(Namespace);
+end;
+
+{ TWitUseItem }
+
+constructor TWitUseItem.Create(const aName: string; const aAlias: String);
+begin
+  inherited create;
+  FName:=aName;
+  FAlias:=aAlias;
+end;
+
+function TWitUseItem.ToString: string;
+begin
+  Result:=Name;
+  if Alias<>'' then
+    Result:=Result+' as '+Alias;
+end;
+
+{ TWITUse }
+
+constructor TWITUse.create;
+begin
+  inherited create;
+  FItems:=TWITUseItemList.Create(True);
+end;
+
+destructor TWITUse.destroy;
+begin
+  FreeAndNil(FITems);
+  inherited destroy;
+end;
+
+function TWITUse.ToString: string;
+var
+  lItem : TWITUseItem;
+begin
+  result:='';
+  for lItem in FItems do
+    begin
+    if Result<>'' then
+      Result:=Result+', ';
+    Result:=Result+lItem.ToString;
+    end;
+  Result:='use '+FPath.ToString+'.{'+Result+'};';
+end;
+
+procedure TWITUse.AddItem(aItem: TWITUseItem);
+begin
+  FItems.Add(aItem);
+end;
+
+procedure TWITUse.AddItem(const aName: string; aAlias: string);
+begin
+  AddItem(TWITUseItem.Create(aName,aAlias));
+end;
+
+{ TWitIncludeItem }
+
+constructor TWitIncludeItem.Create(const aName: string; const aAlias: String = '');
+begin
+  Inherited Create;
+  FName:=aName;
+  FAlias:=aAlias;
+end;
+
+function TWitIncludeItem.ToString: string;
+begin
+  Result:=Name;
+  if Alias<>'' then
+    Result:=Result+' as '+Alias;
+end;
+
+constructor TWITInclude.create();
+begin
+  inherited Create;
+  FPath:=TWITUsePath.Create;
+  FItems:=TWitIncludeItemList.Create(True);
+end;
+
+destructor TWITInclude.destroy;
+begin
+  FreeAndNil(FPath);
+  FreeAndNil(FItems);
+  inherited destroy;
+end;
+
+function TWITInclude.ToString: string;
+var
+  I : integer;
+begin
+  Result:='';
+  For I:=0 to Items.Count-1 do
+    begin
+    if Result<>'' then
+      Result:=Result+' , ';
+    Result:=Result+Items[i].ToString;
+    end;
+  if Result<>'' then
+    Result:=' with {'+Result+'}'
+  else
+    Result:=';';
+  Result:='include '+Path.ToString+Result;
+end;
+
+function TWITInclude.AddItem(const aItem: String; aAlias: String): TWitIncludeItem;
+begin
+  Result:=TWitIncludeItem.Create(aItem,aAlias);
+  FItems.Add(Result);
+end;
+
+end.

+ 1463 - 0
packages/fcl-wit/src/wit.parser.pp

@@ -0,0 +1,1463 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2025 Michael Van Canneyt ([email protected])
+
+    Parser for WIT documents.
+
+    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.
+
+ **********************************************************************}
+unit WIT.Parser;
+
+{$mode objfpc}
+{$h+}
+{$modeswitch advancedrecords}
+
+interface
+
+uses
+  {$IFDEF FPC_DOTTEDUNITS}
+  System.SysUtils,
+  System.Types,
+  {$ELSE}
+  SysUtils,
+  Types,
+  {$ENDIF}
+  WIT.Model,
+  WIT.Scanner;
+
+
+Type
+  EWITParser = class(EWIT);
+
+  { TAnnotationStore }
+
+  TAnnotationStore = Record
+    Items : Array of TWITAnnotation;
+    Count : Integer;
+    class function Create : TAnnotationStore; static;
+    procedure Add(aAnnotation : TWITAnnotation);
+    procedure Apply(aEl : TWITBaseElement);
+    procedure Free;
+  end;
+
+  { TWITParser }
+
+  TWITParser = class
+  Private
+    FOwnsScanner : Boolean;
+    FScanner: TWITScanner;
+    FLastToken : TToken;
+    FUnGet : TToken;
+  protected
+    procedure Error(ErrNo : Integer; const Msg : String);
+    procedure Error(ErrNo : Integer; const Fmt : String; Const Args : Array of const);
+    function GetToken : TToken;
+    procedure UngetToken(aToken : TToken);
+    procedure CheckToken(constref aToken : TToken;aType : TTokenType);
+    procedure CheckTokens(constref aToken : TToken;aTypes : TTokenTypes);
+    function ExpectToken(aType : TTokenType) : TToken;
+    function ExpectToken(aTypes: TTokenTypes): TToken;
+    function ParseInterface(const aName : string = ''): TWITInterface;
+    function ParsePackage: TWITPackage;
+    procedure ParsePackageContent(aPackage : TWITPackage);
+    function ParseAnnotation: TWITAnnotation;
+    function ParseExchange: TWITExchange;
+    function ParseFunctionResult: TWITType;
+    function ParseFunctionParam: TWITFuncParam;
+    function ParseFunctionType(aAllowFlags: TWITFunctionFlags): TWITFunctionType;
+    function ParseFunction(aAllowStatic: boolean=false): TWITFunction;
+    function ParseTopLevelUse: TWITTopLevelUse;
+    function ParseUse: TWITUse;
+    function ParseInclude : TWITInclude;
+    function ParseUsePath(aUse: TWITUsePath; allowAs: boolean; aExtraTerminator: TTokenType=ttEOF): String;
+    function ParseWorld: TWITWorld;
+    // Types
+    function ParseType: TWITType;
+    function ParseHandle(aOwned: Boolean): TWITType;
+    function ParseEnumType: TWITTypeDef;
+    function ParseFlagsType: TWITTypeDef;
+    function ParseListType: TWITListType;
+    function ParseOptionType: TWITOptionType;
+    function ParseFutureType: TWITFutureType;
+    function ParseStreamType: TWITStreamType;
+    function ParseRecordType: TWITTypeDef;
+    function ParseResourceType: TWITTypeDef;
+    function ParseResultType: TWITResultType;
+    function ParseTupleType: TWITTupleType;
+    function ParseVariantType: TWITTypeDef;
+    function ParseTypeDef: TWITTypeDef;
+  protected
+    Property Scanner : TWITScanner read FScanner;
+    property LastToken : TToken Read FLastToken;
+  Public
+    constructor create(aScanner: TWITScanner; aOwnsScanner: Boolean=false);
+    function ParseDocument: TWITDocument;
+    procedure ParseDocument(aDocument: TWITDocument);
+    Destructor destroy; override;
+  end;
+
+const
+  WITERR_EXPECTEDTOKEN  = 1001;
+  WITERR_EXPECTEDTOKENS = 1002;
+  WITERR_EXPECTEDDIV = 1003;
+  WITERR_DOUBLEUNGET = 1004;
+  WITERR_UNEXPECTEDTOKEN = 1005;
+  WITERR_ASNOTALLOWED = 1006;
+  WITERR_NOGATE = 1007;
+
+Resourcestring
+  SErrDoubleUnget = 'UngetToken can be called only once.';
+  SErrUnexpected = 'Unexpected token: %s';
+  SErrExpectedTokenGot = 'Expected token "%s", got: "%s"';
+  SErrExpectedOneOfTokensGot = 'Expected one token of "%s", got: "%s"';
+  SErrorAt = 'Error at %s(%d,%d): %s';
+  SErrExpectedDiv    = 'Expected /, got @';
+  SErrAsNotAllowed = 'As not allowed in this use-path';
+  SErrNoGate = 'A gate is not allowed (double gate)';
+
+implementation
+
+{ TAnnotationStore }
+
+class function TAnnotationStore.Create: TAnnotationStore;
+begin
+  Result.Count:=0;
+  Result.Items:=[];
+end;
+
+procedure TAnnotationStore.Add(aAnnotation: TWITAnnotation);
+begin
+  if Count=Length(Items) then
+    SetLength(Items,Count+3);
+  Items[Count]:=aAnnotation;
+  inc(Count);
+end;
+
+procedure TAnnotationStore.Apply(aEl: TWITBaseElement);
+var
+  I : Integer;
+begin
+  for I:=0 to Count-1 do
+    begin
+    aEl.AddAnnotation(Items[i]);
+    Items[i]:=Nil;
+    end;
+  Count:=0;
+end;
+
+procedure TAnnotationStore.Free;
+var
+  I : Integer;
+begin
+  for I:=0 to Count-1 do
+    FreeAndNil(Items[i]);
+  Count:=0;
+end;
+
+function TWITParser.ParseRecordType: TWITTypeDef;
+var
+  RecordType: TWITRecordType;
+  Field: TWITRecordField;
+  lFieldType : TWITType;
+  lToken: TToken;
+  lName,lFieldName : String;
+
+begin
+  CheckToken(lAstToken,ttRecord);
+  lToken:=ExpectToken(ttIdentifier);
+  lName:=lToken.Value;
+  lToken:=ExpectToken(ttOpenCurlyBrace);
+  RecordType:=TWITRecordType.Create;
+  try
+    lToken:=GetToken;
+    while not (lToken.TokenType in [ttEOF,ttCloseCurlyBrace]) do
+      begin
+      CheckToken(lToken,ttIdentifier);
+      lFieldName:=lToken.Value;
+      ExpectToken(ttColon);
+      GetToken; // Position on first type token
+      lFieldType:=ParseType;
+      Field:=TWITRecordField.Create(lFieldName,lFieldType);
+      RecordType.AddField(Field);
+      lToken:=ExpectToken([ttComma,ttCloseCurlyBrace]);
+      if lToken.TokenType=ttComma then
+        lToken:=GetToken;
+      end;
+    CheckToken(lastToken,ttCloseCurlyBrace);
+  except
+    RecordType.Free;
+    Raise;
+  end;
+  Result:=TWitTypeDef.Create(lName,RecordType);
+end;
+
+function TWITParser.ParseFlagsType: TWITTypeDef;
+(*
+  on entry: flags
+  on exit: }
+*)
+var
+  FlagsType: TWITFlagsType;
+  lToken : TToken;
+  lName : string;
+begin
+  CheckToken(FLastToken,ttFlags);
+  lToken:=ExpectToken(ttIdentifier);
+  lName:=lToken.Value;
+  FlagsType:=TWITFlagsType.Create;
+  try
+    ExpectToken(ttOpenCurlyBrace);
+    lToken:=GetToken;
+    while not (lToken.TokenType in [ttEOF,ttCloseCurlyBrace]) do
+      begin
+      CheckToken(lToken,ttIdentifier);
+      FlagsType.AddFlag(lToken.value);
+      lToken:=GetToken;
+      if (lToken.TokenType=ttComma) then
+        lToken:=GetToken;
+      end;
+  Except
+    FlagsType.Free;
+    Raise;
+  end;
+  Result:=TWitTypeDef.Create(lName,FlagsType);
+end;
+
+function TWITParser.ParseEnumType: TWITTypeDef;
+(*
+  on entry: enum
+  on exit: }
+*)
+var
+  EnumType: TWITEnumType;
+  lToken : TToken;
+  lName : string;
+
+begin
+  Result:=Nil;
+  CheckToken(LastToken,ttEnum);
+  lName:=ExpectToken(ttIdentifier).Value;
+  EnumType:=TWITEnumType.Create();
+  try
+    expectToken(ttOpenCurlyBrace);
+    lToken:=GetToken;
+    while not (lToken.TokenType in [ttEOF,ttCloseCurlyBrace]) do
+      begin
+      CheckToken(lToken,ttIdentifier);
+      EnumType.AddCase(lToken.Value);
+      lToken:=ExpectToken([ttComma,ttCloseCurlyBrace]);
+      if lToken.TokenType=ttComma then
+        lToken:=GetToken;
+      end;
+    CheckToken(lastToken,ttCloseCurlyBrace);
+  except
+    EnumType.Free;
+    Raise;
+  end;
+  Result:=TWITTypeDef.Create(lName,EnumType);
+end;
+
+function TWITParser.ParseTupleType: TWITTupleType;
+{
+  on entry: tuple
+  on exit: >
+}
+var
+  TupleType: TWITTupleType;
+  lToken : TToken;
+begin
+  CheckToken(lastToken,ttTuple);
+  ExpectToken(ttLessThan);
+  TupleType:=TWITTupleType.Create;
+  try
+    lToken:=GetToken;
+    while lToken.TokenType<>ttGreaterThan do
+      begin
+      TupleType.AddItem(ParseType);
+      lToken:=ExpectToken([ttComma,ttGreaterThan]);
+      if lToken.TokenType=ttComma then
+        lToken:=GetToken;
+      end;
+  except
+    TupleType.Free;
+    Raise;
+  end;
+  Result:=TupleType;
+end;
+
+function TWITParser.ParseListType: TWITListType;
+{
+  on entry : list
+  on exit >
+}
+var
+  lToken: TToken;
+begin
+  ExpectToken(ttLessThan);
+  GetToken;
+  Result:=TWITListType.Create(ParseType);
+  lToken:=ExpectToken([ttGreaterThan,ttComma]);
+  if lToken.TokenType=ttComma then
+    begin
+    lToken:=ExpectToken(ttNumber);
+    Result.ItemCount:=StrToInt(lToken.Value);
+    ExpectToken(ttGreaterThan);
+    end;
+end;
+
+function TWITParser.ParseOptionType: TWITOptionType;
+{
+  on entry: Option
+  on exit: >
+}
+var
+  lType : TWITType;
+begin
+  CheckToken(LastToken,ttOption);
+  ExpectToken(ttLessThan);
+  GetToken; // position on first token
+  lType:=ParseType;
+  try
+    ExpectToken(ttGreaterThan);
+    Result:=TWITOptionType.Create(lType);
+    lType:=nil;
+  finally
+    FreeAndNil(lType);
+  end;
+end;
+
+function TWITParser.ParseFutureType: TWITFutureType;
+{
+  on entry: Future
+  on exit: >
+}
+var
+  lType : TWITType;
+  lToken : TToken;
+begin
+  lType:=Nil;
+  CheckToken(LastToken,ttFuture);
+  lToken:=GetToken;
+  if lToken.TokenType<>ttLessThan then
+    begin
+    Result:=TWITFutureType.Create(TWITType.Create(wtVoid));
+    UngetToken(LastToken);
+    end
+  else
+    begin
+    GetToken; // position on first token of type
+    lType:=ParseType;
+    try
+      ExpectToken(ttGreaterThan);
+      Result:=TWITFutureType.Create(lType);
+    except
+      FreeAndNil(lType);
+      Raise;
+    end;
+    end;
+end;
+
+function TWITParser.ParseStreamType: TWITStreamType;
+{
+  on entry: stream
+  on exit: > or stream
+}
+var
+  lType : TWITType;
+  lToken : TToken;
+begin
+  CheckToken(LastToken,ttStream);
+  lToken:=GetToken; // position on first token
+  if lToken.TokenType<>ttLessThan then
+    begin
+    Result:=TWITStreamType.Create(TWITType.Create(wtVoid));
+    UnGetToken(lToken);
+    end
+  else
+    begin
+    GetToken; // Position on first token of type
+    lType:=ParseType;
+    try
+      ExpectToken(ttGreaterThan);
+      Result:=TWITStreamType.Create(lType);
+      lType:=nil;
+    finally
+      FreeAndNil(lType);
+    end;
+    end;
+end;
+
+function TWITParser.ParseResultType: TWITResultType;
+{
+  On entry: result
+  on exit: >
+}
+var
+  lToken : TToken;
+  lOK,lError : TWITType;
+
+begin
+  CheckToken(LastToken,ttResult);
+  lOK:=Nil;
+  lError:=Nil;
+  lToken:=GetToken;
+  if lToken.TokenType=ttLessThan then
+    begin
+    lToken:=GetToken;
+    if not ((lToken.TokenType=ttIdentifier) and (ltoken.Value='_')) then
+      lOK:=ParseType;
+    lToken:=ExpectToken([ttGreaterThan,ttComma]);
+    if lToken.TokenType=ttComma then
+      begin
+      lToken:=GetToken;
+      if not ((lToken.TokenType=ttIdentifier) and (ltoken.Value='_')) then
+        lError:=ParseType;
+      lToken:=ExpectToken(ttGreaterThan);
+      end;
+    end
+  else
+    UnGetToken(lToken);
+  Result:=TWITResultType.Create(lOK,lError);
+end;
+
+function TWITParser.ParseVariantType: TWITTypeDef;
+(*
+  on entry : variant
+  on exit : }
+*)
+var
+  lVariant: TWITVariantType;
+  lCase: TWITVariantCase;
+  lCaseType : TWITType;
+  lToken : TToken;
+  lName,lCaseName : string;
+
+begin
+  CheckToken(LastToken,ttVariant);
+  lToken:=ExpectToken(ttIdentifier);
+  lName:=lToken.Value;
+  lToken:=ExpectToken(ttOpenCurlyBrace);
+  lCaseType:=Nil;
+  lVariant:=TWITVariantType.Create;
+  try
+    lToken:=GetToken;
+    while not (lToken.TokenType in [ttEOF,ttCloseCurlyBrace]) do
+      begin
+      CheckToken(lToken,ttIdentifier);
+      lCaseName:=lToken.Value;
+      lCaseType:=Nil;
+      lToken:=ExpectToken([ttComma,ttCloseCurlyBrace,ttOpenRoundBrace]);
+      if lToken.TokenType=ttOpenRoundBrace then
+        begin
+        GetToken; // Position on first token of type
+        lCaseType:=ParseType;
+        ExpectToken([ttCloseRoundBrace]);
+        lToken:=ExpectToken([ttComma,ttCloseCurlyBrace]);
+        end;
+      lCase:=TWITVariantCase.Create(lCaseName,lCaseType);
+      lCaseType:=Nil;
+      lVariant.AddCase(lCase);
+      if lToken.TokenType=ttComma then
+        lToken:=GetToken;
+      end;
+    CheckToken(lastToken,ttCloseCurlyBrace);
+  except
+    lCaseType.Free;
+    lVariant.Free;
+    Raise;
+  end;
+  Result:=TWITTypeDef.Create(lName,lVariant);
+end;
+
+function TWITParser.ParseHandle(aOwned : Boolean): TWITType;
+{
+  on entry: borrow/own
+  on exit : >
+}
+
+var
+  lToken : TToken;
+
+begin
+  CheckTokens(lastToken,[ttBorrow,ttOwn]);
+  ExpectToken(ttLessThan);
+  lToken:=ExpectToken(ttIdentifier);
+  ExpectToken(ttGreaterThan);
+  Result:=TWITHandleType.Create(lToken.Value,Not aOwned);
+end;
+
+function TWITParser.ParseType: TWITType;
+{
+  On Entry: first token of type
+  On exit : last token of type
+}
+var
+  lToken: TToken;
+begin
+  lToken:=LastToken;
+  Case lToken.TokenType of
+  ttlist:
+    Result:=ParseListType;
+  ttOwn,
+  ttBorrow:
+    Result:=ParseHandle(lToken.TokenType=ttOwn);
+  ttOption:
+    Result:=ParseOptionType;
+  ttFuture:
+    Result:=ParseFutureType;
+  ttStream:
+    Result:=ParseStreamType;
+  ttResult:
+    Result:=ParseResultType;
+  ttResource:
+    Result:=ParseResourceType;
+  ttTuple :
+    Result:= ParseTupleType;
+  ttChar:
+    Result:=TWITType.Create(wtChar);
+  ttBool:
+    Result:=TWITType.Create(wtBool);
+  ttStringType:
+    Result:=TWITType.Create(wtString);
+  ttU8:
+    Result:=TWITType.Create(wtU8);
+  ttU16:
+    Result:=TWITType.Create(wtU16);
+  ttU32:
+    Result:=TWITType.Create(wtU32);
+  ttU64:
+    Result:=TWITType.Create(wtU64);
+  ttS8:
+    Result:=TWITType.Create(wtS8);
+  ttS16:
+    Result:=TWITType.Create(wtS16);
+  ttS32:
+    Result:=TWITType.Create(wtS32);
+  tts64:
+    Result:=TWITType.Create(wtS64);
+  ttf32:
+    Result:=TWITType.Create(wtFloat32);
+  ttf64:
+    Result:=TWITType.Create(wtFloat64);
+  ttIdentifier:
+    Result:=TWITIdentifierType.Create(lToken.Value);
+  else
+    raise Exception.Create('Unexpected Token: ' + lToken.Value);
+  end;
+end;
+
+
+
+function TWITParser.ParsePackage: TWITPackage;
+(*
+  On entry: package
+  Exit : ; or }
+*)
+var
+  lToken: TToken;
+  Package: TWITPackage;
+  PackageName: string;
+  Parts: TStringDynArray;
+begin
+  // Parse 'package' packageName
+  lToken:=LastToken;
+  CheckToken(lToken,ttPackage);
+  PackageName:='';
+  Package:=TWITPackage.Create;
+  try
+    Result:=Package;
+    lToken:=GetToken;
+    while not (lToken.TokenType in [ttEOF,ttSemicolon,ttOpenCurlyBrace]) do
+    begin
+      PackageName:=PackageName + lToken.Value;
+      lToken:=GetToken;
+    end;
+    Parts:=PackageName.Split([':', '@']);
+
+    Case Length(Parts) of
+    1:
+    begin
+      Package.Namespace:='';
+      Package.PackageName:=Parts[0];
+      Package.Version:='';
+    end;
+    2:
+    begin
+      Package.Namespace:=Parts[0];
+      Package.PackageName:=Parts[1];
+      Package.Version:='';
+    end;
+    3:
+    begin
+      Package.Namespace:=Parts[0];
+      Package.PackageName:=Parts[1];
+      Package.Version:=Parts[2];
+    end;
+    else
+      raise Exception.Create('Invalid package name format: ' + PackageName);
+    end;
+
+    CheckTokens(lToken,[ttEOF,ttSemicolon,ttOpenCurlyBrace]);
+    if (lToken.TokenType in [ttSemicolon,ttEOF]) then
+      exit;
+    ParsePackageContent(Package);
+
+    CheckToken(LastToken,ttCloseCurlyBrace);
+  except
+    Package.Free;
+    Raise;
+  end;
+end;
+
+
+function TWITParser.ParseResourceType: TWITTypeDef;
+(*
+  on entry: resource
+  on exit: ; or }
+*)
+var
+  lToken: TToken;
+  lResource: TWITResourceType;
+  lName : string;
+  lFunctionType : TWITFunctionType;
+  lFunction : TWITFunction;
+  lAnn : TAnnotationStore;
+
+begin
+  lAnn:=TAnnotationStore.Create;
+  Checktoken(LastToken,ttResource);
+  lToken:=ExpectToken(ttIdentifier);
+  lName:=lToken.Value;
+  lResource:=TWITResourceType.Create(lName);
+  try
+    lToken:=ExpectToken([ttSemicolon,ttOpenCurlyBrace]);
+    if lToken.TokenType=ttOpenCurlyBrace then
+      begin
+      lToken:=GetToken;
+      while not (lToken.TokenType in [ttEOF,ttCloseCurlyBrace]) do
+        begin
+        lFunction:=Nil;
+        lFunctionType:=Nil;
+        CheckTokens(lToken,[ttIdentifier,ttConstructor,ttAt]);
+        Case lToken.TokenType of
+          ttAt:
+            begin
+            lAnn.Add(ParseAnnotation);
+            lToken:=GetToken;
+            end;
+          ttIdentifier:
+            begin
+            lFunction:=ParseFunction(True);
+            lResource.AddFunction(lFunction);
+            lAnn.Apply(lFunction);
+            lFunction:=Nil;
+            end;
+          ttConstructor:
+            begin
+            lFunctionType:=ParseFunctionType([ffConstructor]);
+            lFunction:=TWITFunction.Create(lName,lFunctionType);
+            lFunctionType:=Nil;
+            lResource.AddFunction(lFunction);
+            lAnn.Apply(lFunction);
+            lFunction:=Nil;
+            end;
+        end;
+        if LastToken.TokenType=ttSemicolon then
+          lToken:=GetToken;
+        end;
+      CheckToken(lastToken,ttCloseCurlyBrace);
+      end;
+    Result:=TWITTypeDef.Create(lName,lResource);
+  except
+    lAnn.Free;
+    lFunctionType.Free;
+    lFunction.Free;
+    lResource.Free;
+    Raise;
+  end;
+end;
+
+
+
+function TWITParser.ParseFunctionParam: TWITFuncParam;
+{
+  on entry: identifier
+  on exit; last token of type
+}
+var
+  lName: String;
+  lType : TWITType;
+begin
+  CheckToken(LastToken,ttIdentifier);
+  lName:=LastToken.Value;
+  ExpectToken(ttColon);
+  GetToken; // Position on first token of type
+  lType:=ParseType;
+  Result:=TWITFuncParam.Create(lName, lType);
+end;
+
+procedure TWITParser.Error(ErrNo: Integer; const Msg: String);
+var
+  E : EWITParser;
+begin
+  E:=EWITParser.CreateFmt(SErrorAt,['',FLastToken.LineNumber,FLastToken.ColumnNumber,Msg]);
+  E.errno:=Errno;
+  Raise E;
+end;
+
+procedure TWITParser.Error(ErrNo: Integer; const Fmt: String;
+  const Args: array of const);
+begin
+  Error(Errno,Format(Fmt,Args));
+end;
+
+
+function TWITParser.ParseFunctionResult: TWITType;
+begin
+  Result:=ParseType;
+end;
+
+function TWITParser.ParseFunctionType(aAllowFlags: TWITFunctionFlags): TWITFunctionType;
+{
+  on entry: func, async, static, constructor
+  on exit : semicolon
+}
+const
+  FlagTokens : Array[TWITFunctionFlag] of TTokenType = (ttAsync, ttStatic, ttConstructor);
+var
+  lTokens : Set of TTokenType;
+  lToken : TToken;
+  lType : TWITFunctionType;
+  lFlag : TWITFunctionFlag;
+  lFlags : TWITFunctionFlags;
+begin
+  lTokens:=[ttFunc];
+  for lFlag in aAllowFlags do
+    Include(lTokens,FLagTokens[lFlag]);
+  lToken:=LastToken;
+  CheckTokens(lToken,lTokens);
+  Exclude(lTokens,ttStatic);
+  lFlags:=[];
+  if (lToken.TokenType=ttStatic) then
+    Include(lFlags,ffStatic);
+  if ffStatic in lFlags then
+    lToken:=ExpectToken(lTokens);
+  Exclude(lTokens,ttAsync);
+  if lToken.TokenType=ttAsync then
+    begin
+    Include(lFlags,ffAsync);
+    lToken:=ExpectToken(lTokens);
+    end;
+  if lToken.TokenType=ttConstructor then
+    Include(lFlags,ffConstructor);
+  ExpectToken(ttOpenRoundBrace);
+  lType:=TWITFunctionType.Create;
+  try
+    lType.Flags:=lFlags;
+    lToken:=GetToken;
+    while lToken.TokenType <> ttCloseRoundBrace do
+    begin
+      lType.Parameters.Add(ParseFunctionParam);
+      lToken:=ExpectToken([ttComma,ttCloseRoundBrace]);
+      if lToken.TokenType = ttComma then
+        lToken:=GetToken;
+    end;
+    lToken:=ExpectToken([ttArrow,ttSemicolon]);
+    if lToken.TokenType = ttArrow then
+      begin
+      GetToken; // position on first token of type
+      lType.ResultType:=ParseType;
+      ExpectToken([ttSemicolon]);
+      end;
+     Result:=lType;
+  except
+    lType.Free;
+    Raise;
+  end;
+  // Parse results (optional)
+end;
+
+function TWITParser.ParseFunction(aAllowStatic : boolean = false): TWITFunction;
+{
+  On Entry: identifier
+  on exit: semicolon
+}
+var
+  lName : string;
+  lFunction: TWITFunction;
+  lFunctionType : TWITFunctionType;
+begin
+  CheckToken(LastToken,ttIdentifier);
+  lName:=LastToken.Value;
+  ExpectToken(ttColon);
+  GetToken;
+  lFunctionType:=ParseFunctionType([ffStatic,ffAsync,ffConstructor]);
+  try
+    lFunction:=TWITFunction.Create(lName,lFunctionType);
+    lFunctionType:=Nil;
+    Result:=lFunction;
+    lFunction:=Nil;
+  except
+    lFunctionType.Free;
+    lFUnction.Free;
+    Raise;
+  end;
+end;
+
+function TWITParser.GetToken: TToken;
+begin
+  if FUnget.TokenType<>ttUnknown then
+    begin
+    FLastToken:=FUnGet;
+    FUnGet:=Default(TToken);
+    end
+  else
+    FLastToken:=Scanner.GetToken;
+  Result:=FLastToken;
+end;
+
+procedure TWITParser.UngetToken(aToken: TToken);
+begin
+  if FUnGet.TokenType<>ttUnknown then
+    Error(WITERR_DOUBLEUNGET,SErrDoubleUnget);
+  FUnGet:=aToken;
+end;
+
+procedure TWITParser.CheckToken(constref aToken: TToken; aType: TTokenType);
+begin
+  if aToken.TokenType<>aType then
+    Error(WITERR_EXPECTEDTOKEN,SErrExpectedTokenGot,[aType.ToString,aToken.Value]);
+end;
+
+procedure TWITParser.CheckTokens(constref aToken: TToken; aTypes: TTokenTypes);
+begin
+  if not (aToken.TokenType in aTypes) then
+    Error(WITERR_EXPECTEDTOKENS,SErrExpectedOneOfTokensGot,[aTypes.ToString,aToken.Value]);
+end;
+
+function TWITParser.ExpectToken(aType: TTokenType): TToken;
+begin
+  Result:=GetToken;
+  CheckToken(Result,aType);
+end;
+
+function TWITParser.ExpectToken(aTypes: TTokenTypes): TToken;
+begin
+  Result:=GetToken;
+  CheckTokens(Result,aTypes);
+end;
+
+constructor TWITParser.create(aScanner: TWITScanner; aOwnsScanner : Boolean = false);
+begin
+  FOwnsScanner:=aOwnsScanner;
+  FScanner:=aScanner;
+end;
+
+destructor TWITParser.destroy;
+begin
+  if FOwnsScanner then
+    FreeAndNil(FScanner);
+  inherited destroy;
+end;
+
+
+function TWITParser.ParseAnnotation: TWITAnnotation;
+{
+  On Entry : @
+  On Exit : )
+}
+var
+  lToken: TToken;
+  Annotation: TWITAnnotation;
+  lArgName,lArgValue : string;
+begin
+  CheckToken(LastToken,ttAt);
+  lToken:=ExpectToken(ttIdentifier);
+  Annotation:=TWITAnnotation.Create(lToken.Value);
+  try
+    ExpectToken(ttOpenRoundBrace);
+    lToken:=GetToken;
+    while (lToken.TokenType = ttIdentifier) do
+        begin
+        lArgName:=lToken.Value;
+        lToken:=GetToken;
+        if lToken.TokenType = ttEqual then
+          begin
+          lToken:=GetToken;
+          if (lToken.TokenType in [ttStringLiteral, ttIdentifier, ttNumber]) then
+            lArgValue:=lToken.Value;
+          end;
+        Annotation.Arguments.Add(TWITAnnotationArgument.Create(lArgName, lArgValue));
+        lToken:=GetToken;
+        if lToken.TokenType = ttComma then
+          lToken:=GetToken
+        else if lToken.TokenType = ttCloseRoundBrace then
+          break
+        else
+          raise Exception.Create('Expected "," or ")", found: ' + lToken.Value);
+        end;
+  except
+    Annotation.Free;
+    Raise;
+  end;
+  Result:=Annotation;
+end;
+
+function TWITParser.ParseTypeDef : TWITTypeDef;
+{
+  first token: Type
+  last token: semicolon
+}
+var
+  lToken : TToken;
+  aType : TTokenType;
+begin
+  CheckTokens(LastToken,[ttType,ttFlags,ttVariant,ttResource, ttRecord,ttEnum]);
+  aType:=lastToken.TokenType;
+  case aType of
+    ttEnum:
+      Result:=ParseEnumType;
+    ttRecord:
+      Result:=ParseRecordType;
+    ttFlags:
+      Result:=ParseFlagsType;
+    ttVariant:
+      Result:=ParseVariantType;
+    ttResource:
+      Result:=ParseResourceType;
+    ttType:
+      begin
+      lToken:=ExpectToken(ttIdentifier);
+      ExpectToken(ttEqual);
+      GetToken;// Position on first token of type.
+      Result:=TWITTypeDef.Create(lToken.Value,ParseType);
+      ExpectToken(ttSemicolon);
+      end;
+  end;
+end;
+
+function TWITParser.ParseInterface(const aName : string = ''): TWITInterface;
+(*
+  On entry: interface
+  On exit: }
+*)
+const
+  TypeDefTokens = [ttEnum, ttResource, ttVariant, ttRecord, ttFlags, ttType, ttUse];
+  InterfaceTokens = [ttAt,ttIdentifier] + TypeDefTokens;
+var
+  lToken: TToken;
+  lInterface: TWITInterface;
+  lAnnotation : TAnnotationStore;
+  lFunc : TWITFunction;
+  lType : TWITTypeDef;
+  lUse : TWITUse;
+  lName : string;
+begin
+  lInterface:=Nil;
+  lFunc:=nil;
+  lType:=nil;
+  lUse:=nil;
+  lAnnotation:=TAnnotationStore.Create;
+  lToken:=LastToken;
+  CheckToken(lToken,ttInterface);
+  lName:=aName;
+  if lName='' then
+    begin
+    lToken:=ExpectToken(ttIdentifier);
+    lName:=lToken.Value;
+    end;
+  lInterface:=TWITInterface.Create(lName);
+  try
+    ExpectToken(ttOpenCurlyBrace);
+    lToken:=GetToken;
+    while (lToken.TokenType in InterfaceTokens) do
+    begin
+      Case lToken.TokenType of
+      ttAt:
+        begin
+        lAnnotation.Add(ParseAnnotation);
+        lToken:=GetToken;
+        end;
+      ttType,ttFlags,ttEnum,ttRecord,ttVariant,ttResource:
+        begin
+        lType:=ParseTypeDef;
+        lInterface.AddType(lType);
+        lAnnotation.Apply(lType);
+        lToken:=GetToken;
+        end;
+      ttUse :
+        begin
+        lUse:=ParseUse;
+        lAnnotation.Apply(lUse);
+        lInterface.AddUses(lUse);
+        lUse:=Nil;
+        lToken:=GetToken;
+        end;
+      ttIdentifier:
+        begin
+        lFunc:=ParseFunction;
+        lAnnotation.Apply(lFunc);
+        lInterface.AddFunction(lFunc);
+        lToken:=GetToken;
+        end;
+      end;
+      lToken:=FLastToken;
+    end;
+    Result:=lInterface;
+  except
+    LInterface.Free;
+    lAnnotation.Free;
+    Raise;
+  end;
+  CheckToken(lToken,ttCloseCurlyBrace);
+end;
+
+function TWITParser.ParseDocument: TWITDocument;
+var
+  lDocument: TWITDocument;
+begin
+  lDocument:=TWITDocument.Create;
+  try
+    ParseDocument(lDocument);
+    Result:=lDocument;
+    lDocument:=Nil;
+  finally
+    lDocument.Free;
+  end;
+end;
+
+procedure TWITParser.ParseDocument(aDocument: TWITDocument);
+var
+  Token: TToken;
+  lInterface : TWITInterface;
+  lAnn: TAnnotationStore;
+  lWorld : TWITWorld;
+  lPackage : TWITPackage;
+  lUse : TWITTopLevelUse;
+begin
+  lAnn:=TAnnotationStore.Create;
+  Scanner.SkipWhitespace:=True;
+  Token:=GetToken;
+  while Token.TokenType <> ttEOF do
+    begin
+      if Token.TokenType=ttAt then
+        begin
+        lAnn.Add(ParseAnnotation);
+        Token:=GetToken;
+        end;
+      Case Token.TokenType of
+      ttPackage:
+        begin
+        lPackage:=ParsePackage;
+        lAnn.Apply(lPackage);
+        aDocument.Packages.add(lPackage);
+        if not lPackage.IsNested then
+          aDocument.DefaultPackage:=lPackage;
+        end;
+      ttUse:
+        begin
+        lUse:=ParseTopLevelUse;
+        lAnn.Apply(lUse);
+        aDocument.AddUse(lUse);
+        end;
+      ttInterface:
+        begin
+        lInterface:=ParseInterface;
+        aDocument.AddInterface(lInterface);
+        lAnn.Apply(lInterface);
+        end;
+      ttWorld:
+        begin
+        lWorld:=ParseWorld;
+        aDocument.AddWorld(lWorld);
+        lAnn.Apply(lWorld);
+        end;
+      else
+        raise Exception.Create('Unexpected token: ' + Token.Value);
+      end;
+      Token:=GetToken;
+    end;
+end;
+
+function TWITParser.ParseUsePath(aUse: TWITUsePath; allowAs: boolean; aExtraTerminator : TTokenType = ttEOF ): String;
+{
+  on entry: first ID
+  on exit: semicolon
+}
+type
+  TUsePart = (upUnknown,upNamespace,upIdentifier,UpVersion,upAlias);
+
+var
+  lNameToken,lToken: TToken;
+  lPart : TUsePart;
+  lTerminators : Set of TTokenType;
+
+  procedure addlastpart;
+  begin
+    Case lPart of
+      upUnknown,
+      upIdentifier : aUse.Identifier:=lNameToken.Value;
+      upVersion : aUse.Version:=lNameToken.Value;
+      upAlias : Result:=lNameToken.Value;
+    end;
+  end;
+
+begin
+  lTerminators:=[ttSemicolon,ttEOF];
+  include(lTerminators,aExtraTerminator);
+  lPart:=upUnknown;
+  lNameToken:=LastToken;
+  lToken:=GetToken;
+  While not (lToken.TokenType in lTerminators) do
+    begin
+    Case lToken.TokenType of
+     ttColon:
+       begin
+       aUse.AddNamespace(lNameToken.Value);
+       end;
+     ttDiv :
+       begin
+       aUse.PackageName:=lNameToken.Value;
+       lPart:=upIdentifier;
+       end;
+     ttAt:
+       begin
+       if lPart<>upIdentifier then
+         Error(WITERR_EXPECTEDDIV,SErrExpectedDiv);
+       aUse.Identifier:=lNameToken.Value;
+       lpart:=upVersion;
+       end;
+     ttAs:
+       begin
+       if not AllowAs then
+         Error(WITERR_ASNOTALLOWED,SERRAsNotAllowed);
+       Case lPart of
+         upUnknown,
+         upIdentifier,
+         UpVersion : AddLastPart;
+       else
+         Error(WITERR_EXPECTEDDIV,SErrExpectedDiv);
+       end;
+       lpart:=upAlias;
+       end;
+    end;
+    lNameToken:=ExpectToken([ttIdentifier,ttDiv,ttAt,ttColon,ttSemicolon,ttNumber]);
+    if lNameToken.TokenType in [ttIdentifier,ttNumber] then
+      lToken:=GetToken
+    else
+      lToken:=lNameToken;
+    end;
+  AddLastpart;
+end;
+
+
+function TWITParser.ParseTopLevelUse: TWITTopLevelUse;
+{
+  on entry: use
+  on exit: semicolon
+}
+var
+  UseStatement: TWITTopLevelUse;
+  lRename : string;
+begin
+  CheckToken(LastToken,ttUse);
+  UseStatement:=TWITTopLevelUse.Create;
+  try
+    ExpectToken(ttIdentifier);
+    lRename:=ParseUsePath(UseStatement.Path,True);
+    UseStatement.Rename:=lRename;
+  except
+    UseStatement.Free;
+    Raise;
+  end;
+  Result:=UseStatement;
+end;
+
+function TWITParser.ParseUse: TWITUse;
+{
+  on entry: use
+  on exit: semicolon
+}
+var
+  UseStatement: TWITUse;
+  lToken : TToken;
+  lName,lAlias : string;
+
+begin
+  CheckToken(LastToken,ttUse);
+  UseStatement:=TWITUse.Create;
+  try
+    ExpectToken(ttIdentifier);
+    ParseUsePath(UseStatement.Path,false,ttDot);
+    CheckToken(LastToken,ttDot);
+    ExpectToken(ttOpenCurlyBrace);
+    lToken:=GetToken;
+    While not (lToken.TokenType in [ttEOF,ttCloseCurlyBrace]) do
+      begin
+      CheckToken(lToken,ttIdentifier);
+      lAlias:='';
+      lName:=lToken.Value;
+      lToken:=GetToken;
+      if lToken.TokenType=ttAs then
+        begin
+        lToken:=ExpectToken(ttIdentifier);
+        lAlias:=lToken.Value;
+        lToken:=GetToken;
+        end;
+      UseStatement.AddItem(lName,lAlias);
+      CheckTokens(lToken,[ttComma,ttCloseCurlyBrace]);
+      if (lToken.TokenType=ttComma) then
+        lToken:=GetToken;
+      end;
+    CheckToken(LastToken,ttCloseCurlyBrace);
+    ExpectToken(ttSemicolon);
+  except
+    UseStatement.Free;
+    Raise;
+  end;
+  Result:=UseStatement;
+end;
+
+function TWITParser.ParseInclude: TWITInclude;
+var
+  lToken : TToken;
+  lName : string;
+begin
+  CheckToken(LastToken,ttInclude);
+  ExpectToken(ttIdentifier);
+  Result:=TWITInclude.Create;
+  try
+    ParseUsePath(Result.Path,False,ttWith);
+    if (LastToken.TokenType=ttWith) then
+      begin
+      ExpectToken(ttOpenCurlyBrace);
+      lToken:=GetToken;
+      While not (lToken.TokenType in [ttCloseCurlyBrace,ttEOF]) do
+        begin
+        CheckToken(lToken,ttIdentifier);
+        lName:=lToken.Value;
+        ExpectToken(ttAs);
+        lToken:=ExpectToken(ttIdentifier);
+        Result.AddItem(lName,lToken.Value);
+        lToken:=ExpectToken([ttComma,ttCloseCurlyBrace]);
+        if (lToken.TokenType=ttComma) then
+          lToken:=GetToken;
+        end;
+      CheckToken(LastToken,ttCloseCurlyBrace);
+      end;
+  except
+    Result.Free;
+    Raise;
+  end;
+end;
+
+function TWITParser.ParseExchange: TWITExchange;
+var
+  lToken : TToken;
+  lName : String;
+  lFunc : TWITFunctionType;
+  lIntf : TWITInterface;
+  LUse : TWITUsePath;
+  XT : TExchangeType;
+begin
+  lFunc:=Nil;
+  lIntf:=Nil;
+  try
+    CheckTokens(LastToken,[ttExport,ttImport]);
+    if LastToken.TOkenType=ttExport then
+      XT:=xtExport
+    else
+      XT:=xtImport;
+    lToken:=ExpectToken(ttIdentifier);
+    lName:=lToken.Value;
+    lToken:=ExpectToken([ttColon,ttSemicolon]);
+    if lToken.TokenType=ttSemicolon then
+      begin
+      Result:=TWITExchangeIdentifier.Create(XT,LName);
+      end
+    else
+      begin
+      lToken:=GetToken;
+      case lToken.TokenType of
+        ttIdentifier :
+          begin
+          lUse:=TWitUsePath.Create;
+          lUse.AddNamespace(lName);
+          ParseUsePath(lUse,False);
+          Result:=TWITExchangeIdentifier.Create(XT,lUse);
+          lUse:=nil;
+          end;
+        ttStatic, ttAsync, ttFunc :
+          begin
+          lFunc:=ParseFunctionType([ffStatic,ffAsync]);
+          Result:=TWITExchangeFunc.Create(XT,lName,lFunc);
+          lFunc:=nil;
+          end;
+        ttInterface :
+          begin
+          lIntf:=ParseInterface(lName);
+          Result:=TWITExchangeInterface.Create(XT,lName,lIntf);
+          lIntf:=nil;
+          end;
+      end;
+      end;
+  except
+    lFunc.Free;
+    lIntf.Free;
+    lUse.Free;
+    Raise;
+  end;
+end;
+
+function TWITParser.ParseWorld: TWITWorld;
+
+var
+  Token: TToken;
+  lWorld: TWITWorld;
+  lUse : TWitUse;
+  LAnn : TAnnotationStore;
+  lType : TWITTypeDef;
+  LExchange : TWITExchange;
+  lInclude : TWITInclude;
+
+begin
+  CheckToken(LastToken,ttWorld);
+  Token:=ExpectToken(ttIdentifier);
+  lWorld:=TWITWorld.Create(Token.Value);
+  LAnn:=TAnnotationStore.Create;
+  lUse:=Nil;
+  lExchange:=Nil;
+  lInclude:=nil;
+  try
+    Token:=ExpectToken(ttOpenCurlyBrace);
+    Token:=GetToken;
+    while Not (Token.TokenType in [ttEOF,ttCloseCurlyBrace]) do
+      begin
+      case Token.TokenType of
+        ttAt:
+          begin
+          lAnn.Add(ParseAnnotation);
+          end;
+        ttInclude:
+          begin
+          lInclude:=ParseInclude;
+          lAnn.Apply(lInclude);
+          lWorld.AddInclude(lInclude);
+          lInclude:=Nil;
+         end;
+        ttImport:
+          begin
+          lExchange:=ParseExchange;
+          lAnn.Apply(lExchange);
+          lWorld.AddImport(lExchange);
+          lExchange:=Nil;
+          end;
+        ttExport:
+          begin
+          lExchange:=ParseExchange;
+          lAnn.Apply(lExchange);
+          lWorld.AddExport(lExchange);
+          lExchange:=Nil;
+          end;
+{        ttInterface:
+          begin
+          lInterface:=ParseInterface;
+          MaybeGate(lInterface);
+          lWorld.AddInterface(lInterface);
+          end;
+        ttIdentifier:
+          begin
+          lFunction:=ParseFunction;
+          MaybeGate(lFunction);
+          lWorld.AddFunction(lFunction);
+          end;}
+        ttUse:
+          begin
+          lUse:=ParseUse;
+          lAnn.Apply(lUse);
+          lWorld.AddUses(lUse);
+          lUse:=Nil;
+          end;
+        ttType,ttFlags,ttEnum,ttRecord,ttVariant,ttResource:
+          begin
+          lType:=ParseTypeDef;
+          lAnn.Apply(lType);
+          lWorld.AddTypeDef(lType);
+          lType:=Nil;
+          end;
+      else
+        Error(WITERR_UNEXPECTEDTOKEN,Format(SErrUnexpected,[Token.Value]));
+      end;
+      Token:=GetToken;
+      end;
+    CheckToken(lastToken,ttCloseCurlyBrace);
+  except
+    lUse.Free;
+    lExchange.Free;
+    lAnn.Free;
+    lType.Free;
+    lWorld.Free;
+    Raise;
+  end;
+  Result:=lWorld;
+end;
+
+procedure TWITParser.ParsePackageContent(aPackage: TWITPackage);
+var
+  Token: TToken;
+  lInterface : TWITInterface;
+  Gate: TWITAnnotation;
+  lWorld : TWITWorld;
+  lUse : TWITTopLevelUse;
+begin
+  aPackage.IsNested:=True;
+  Token:=GetToken;
+  while not (Token.TokenType in [ttEOF,ttCloseCurlyBrace]) do
+    begin
+      Gate:=nil;
+      if Token.TokenType=ttAt then
+        begin
+        Gate:=ParseAnnotation;
+        Token:=GetToken;
+        end;
+      Case Token.TokenType of
+      ttUse:
+        begin
+        lUse:=ParseTopLevelUse;
+        aPackage.UseStatements.Add(luse);
+        if Gate <> nil then
+          begin
+          Gate.Free;
+          Raise EWITParser.Create('Use statement cannot be gated');
+          end;
+        end;
+      ttInterface:
+        begin
+        lInterface:=ParseInterface;
+        aPackage.Interfaces.Add(lInterface);
+        if Gate <> nil then
+          lInterface.AddAnnotation(Gate);
+        end;
+      ttWorld:
+        begin
+        lWorld:=ParseWorld;
+        aPackage.Worlds.Add(lWorld);
+        if Gate <> nil then
+          lWorld.AddAnnotation(Gate);
+        end;
+      else
+        raise Exception.Create('Unexpected token: ' + Token.Value);
+      end;
+      Token:=GetToken;
+    end;
+end;
+
+end.
+
+

+ 557 - 0
packages/fcl-wit/src/wit.scanner.pp

@@ -0,0 +1,557 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2025 Michael Van Canneyt ([email protected])
+
+    Tokenizer for WIT documents.
+
+    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.
+
+ **********************************************************************}
+unit WIT.Scanner;
+
+{$mode objfpc}
+{$h+}
+{$modeswitch typehelpers}
+interface
+
+uses
+  {$IFDEF FPC_DOTTEDUNITS}
+  System.SysUtils, System.Classes, Fcl.Streams.Extra;
+  {$ELSE}
+  SysUtils, Classes, Streamex;
+  {$ENDIF}
+
+
+type
+  EWITScanner = Class(Exception);
+
+  TTokenType = (
+    ttUnknown,
+    ttWhitespace,
+    ttComment,
+    ttEOF,
+    // Symbols
+    ttOpenRoundBrace,
+    ttCloseRoundBrace,
+    ttOpenCurlyBrace,
+    ttCloseCurlyBrace,
+    ttOpenSquareBrace,
+    ttCLoseSquareBrace,
+    ttColon,
+    ttSemicolon,
+    ttComma,
+    ttEqual,
+    ttDot,
+    ttPlus,
+    ttMinus,
+    ttStar,
+    ttDiv,
+    ttLessThan,
+    ttGreaterThan,
+    ttAt,
+    ttArrow,
+    // General
+    ttNumber,
+    ttStringLiteral,
+    ttIdentifier,
+    // Keywords
+    ttAs,
+    ttAsync,
+    ttBool,
+    ttBorrow,
+    ttChar,
+    ttConstructor,
+    ttEnum,
+    ttExport,
+    ttF32,
+    ttF64,
+    ttFlags,
+    ttFrom,
+    ttFunc,
+    ttFuture,
+    ttImport,
+    ttInclude,
+    ttInterface,
+    ttList,
+    ttOption,
+    ttOwn,
+    ttPackage,
+    ttRecord,
+    ttResource,
+    ttResult,
+    ttS8,
+    ttS16,
+    ttS32,
+    ttS64,
+    ttStatic,
+    ttStream,
+    ttStringType,
+    ttTuple,
+    ttType,
+    ttU8,
+    ttU16,
+    ttU32,
+    ttU64,
+    ttUse,
+    ttVariant,
+    ttWith,
+    ttWorld
+
+
+  );
+  TTokenTypes = Set of TTokenType;
+  TKeyWordType = ttAs..ttWorld;
+  TSymbolType = ttOpenRoundBrace..ttArrow;
+
+  TToken = record
+    TokenType: TTokenType;
+    Value: string;
+    LineNumber: Integer;
+    ColumnNumber: Integer;
+  end;
+
+  { TWITScanner }
+
+  TWITScanner = class
+  private
+    FReader: TTextReader;
+    FCurrentLine : AnsiString;
+    FLineNumber: Integer;
+    FColumnNumber: Integer;
+    FCurrentChar: AnsiChar;
+    FSkipWhitespace: Boolean;
+    function GetNewToken(aType: TTokenType): TToken;
+    function GetNextLine: boolean;
+    function HandleIdentifier: TToken;
+    function HandleNumber: TToken;
+    function HandleString: TToken;
+    function HandleWhiteSpace: TToken;
+    function HandleComment: TToken;
+
+    function IsKeyWord(const aValue: string; out aKeyWord: TTokenType): Boolean;
+    function IsSymbol(const aValue: char; out aSymbol: TTokenType): Boolean;
+  protected
+    function GetTokenInternal: TToken;virtual;
+    function GetNextChar: boolean;
+    function PeekNextChar: AnsiChar;
+  public
+    // Scanner always owns the reader
+    constructor Create(Reader: TTextReader);
+    // Scanner does not own the stream;
+    constructor Create(Stream : TStream);
+    // Pass WIT content.
+    constructor Create(WIT : String);
+    destructor Destroy; override;
+    function GetToken: TToken;
+    property LineNumber: Integer read FLineNumber;
+    property ColumnNumber: Integer read FColumnNumber;
+    property SkipWhitespace : Boolean read FSkipWhitespace Write FSkipWhitespace;
+  end;
+
+  { TTokenTypeHelper }
+
+  TTokenTypeHelper = type helper for TTokenType
+    function tostring : string;
+  end;
+
+  { TTokenTypesHelper }
+
+  TTokenTypesHelper = type helper for TTokenTypes
+    function tostring : string;
+  end;
+
+implementation
+
+Resourcestring
+  SErrInvalidCommentChar = 'Invalid comment character: ';
+
+const
+  Keywords: array[TKeyWordType] of string =
+    ( 'as',
+      'async',
+      'bool',
+      'borrow',
+      'char',
+      'constructor',
+      'enum',
+      'export',
+      'f32',
+      'f64',
+      'flags',
+      'from',
+      'func',
+      'future',
+      'import',
+      'include',
+      'interface',
+      'list',
+      'option',
+      'own',
+      'package',
+      'record',
+      'resource',
+      'result',
+      's8',
+      's16',
+      's32',
+      's64',
+      'static',
+      'stream',
+      'string',
+      'tuple',
+      'type',
+      'u8',
+      'u16',
+      'u32',
+      'u64',
+      'use',
+      'variant',
+      'with',
+      'world');
+  Symbols: array[TSymbolType] of string = ('(', ')', '{', '}', '[', ']', ':', ';', ',', '=', '.', '+', '-', '*', '/', '<', '>', '@','->');
+  WhitespaceChars: set of char = [' ', #9, #10, #13];
+
+function IsIdentifierStart(c: char): boolean;
+begin
+  IsIdentifierStart := (c in ['a'..'z', 'A'..'Z', '_','%']);
+end;
+
+function IsIdentifierChar(c: char): boolean;
+begin
+  IsIdentifierChar := (IsIdentifierStart(c) or (c in ['0'..'9','-']));
+end;
+
+function IsDigit(c: char): boolean;
+begin
+  IsDigit := (c in ['0'..'9']);
+end;
+
+function IsWhitespace(c: char): boolean;
+begin
+  IsWhitespace := (c in WhitespaceChars);
+end;
+
+{ TWITScanner }
+
+constructor TWITScanner.Create(Reader: TTextReader);
+begin
+  FReader := Reader;
+  FLineNumber := 0;
+  FColumnNumber := 0;
+  GetNextChar;
+end;
+
+constructor TWITScanner.Create(Stream: TStream);
+
+begin
+  Create(TStreamReader.Create(Stream));
+end;
+
+constructor TWITScanner.Create(WIT: String);
+var
+  S: TStringStream;
+
+begin
+  S:=TStringStream.Create(WIT);
+  try
+    Create(S);
+  finally
+    S.Free;
+  end;
+end;
+
+function TWITScanner.GetNextLine : boolean;
+begin
+  FCurrentLine:='';
+  While (FCurrentLine='') and not FReader.Eof do
+    begin
+    inc(FLineNumber);
+    FCurrentLine:=FReader.ReadLine;
+    end;
+  Result:=(FCurrentLine<>'');
+  FColumnNumber:=0;
+end;
+
+function TWITScanner.GetNextChar : boolean;
+begin
+  Result:=FColumnNumber<Length(FCurrentLine);
+  if not Result then
+    Result:=GetNextLine;
+  if not Result then
+    exit;
+  inc(FColumnNumber);
+  FCurrentChar:=FCurrentLine[FColumnNumber];
+end;
+
+function TWITScanner.PeekNextChar: AnsiChar;
+begin
+  if (FColumnNumber<Length(FCurrentLine)) then
+    Result:=FCurrentLine[FColumnNumber+1]
+  else
+    Result:=#0;
+end;
+
+destructor TWITScanner.Destroy;
+begin
+  FReader.Free;
+  inherited Destroy;
+end;
+
+function TWITScanner.IsKeyWord(const aValue : string; out aKeyWord : TTokenType) : Boolean;
+var
+  KW : TKeywordType;
+begin
+  for kw:=Low(TKeyWordType) to High(TKeywordType) do
+    begin
+    Result:=KeyWords[kw]=aValue;
+    if Result then
+      begin
+      aKeyword:=kw;
+      break;
+      end;
+    end;
+end;
+
+function TWITScanner.IsSymbol(const aValue: char; out aSymbol: TTokenType): Boolean;
+var
+  Sym : TSymbolType;
+begin
+  for Sym:=Low(TSymbolType) to High(TSymbolType) do
+    begin
+    Result:=Symbols[Sym]=aValue;
+    if Result then
+      begin
+      aSymbol:=sym;
+      Break;
+      end;
+    end;
+  if (aSymbol=ttMinus) and (PeekNextChar='>') then
+    begin
+    GetNextChar;
+    aSymbol:=ttArrow;
+    end;
+end;
+
+
+function TWITScanner.GetNewToken(aType : TTokenType) : TToken;
+
+begin
+  Result.TokenType:=aType;
+  Result.ColumnNumber:=FColumnNumber;
+  Result.LineNumber:=FLineNumber;
+  Result.Value:='';
+end;
+
+function TWITScanner.HandleWhiteSpace: TToken;
+
+var
+  lTokenValue : string;
+begin
+  lTokenValue:='';
+  Result:=GetNewToken(ttWhitespace);
+  while IsWhitespace(FCurrentChar) do
+    begin
+    lTokenValue := lTokenValue + FCurrentChar;
+    if Not GetNextChar then
+      FCurrentChar := #0;
+    end;
+  Result.Value := lTokenValue;
+end;
+
+
+function TWITScanner.HandleComment: TToken;
+
+var
+  Level : Integer;
+
+begin
+  Result:=GetNewToken(ttComment);
+  if not GetNextChar then
+    Raise EWITScanner.Create(SErrInvalidCommentChar+'EOF');
+  if (FCurrentChar='/') then
+    begin
+    Result.Value:=Copy(FCurrentLine,FColumnNumber+1,Length(FCurrentLine)-FColumnNumber);
+    FColumnNumber:=Length(FCurrentLine);
+    if not GetNextChar then
+      FCurrentChar:=#0;
+    end
+  else if (FCurrentChar<>'*') then
+    Raise EWITScanner.Create(SErrInvalidCommentChar+FCurrentChar)
+  else
+    begin
+    Level:=1;
+    While Level<>0 do
+      begin
+      While (GetNextChar) and not ((FCurrentChar='*') and (PeekNextChar='/')) do
+        begin
+        if (FCurrentChar='/') and (PeekNextChar='*') then
+          Inc(Level);
+        if FColumnNumber=1 then
+          Result.Value:=Result.Value+#10;
+        Result.Value:=Result.Value+FCurrentChar;
+        end;
+      if Level>1 then
+        Result.Value:=Result.Value+'*'; // the / will be added in the next loop
+      Dec(Level);
+      end;
+    GetNextChar;
+    if (FCurrentChar<>'/') then
+      Raise EWITScanner.Create(SErrInvalidCommentChar+FCurrentChar);
+    if not GetNextChar then
+      FCurrentChar:=#0;
+
+    end;
+
+end;
+
+function TWITScanner.HandleIdentifier: TToken;
+
+var
+  lTokenValue : string;
+begin
+  Result:=GetNewToken(ttIdentifier);
+  lTokenValue:='';
+  while IsIdentifierChar(FCurrentChar) do
+  begin
+    lTokenValue := lTokenValue + FCurrentChar;
+    if not GetNextChar then
+      FCurrentChar := #0;
+  end;
+  if not IsKeyWord(lTokenValue,Result.TokenType) then
+    begin
+    Result.TokenType := ttIdentifier;
+    if lTokenValue[1]='%' then
+      Delete(lTokenValue,1,1);
+    end;
+  Result.Value := lTokenValue;
+end;
+
+function TWITScanner.HandleNumber: TToken;
+
+var
+  lTokenValue : string;
+  lDotCount : integer;
+
+begin
+  lDotCount:=0;
+  Result:=GetNewToken(ttNumber);
+  lTokenValue:='';
+  while IsDigit(FCurrentChar) or ((FCurrentChar='.') and (lDotCount<2)) do
+    begin
+    if (FCurrentChar='.') then
+      Inc(lDotCount);
+    lTokenValue := lTokenValue + FCurrentChar;
+    if not GetNextChar then
+     FCurrentChar := #0;
+    end;
+  Result.Value := lTokenValue;
+  if lDotCount>1 then
+    Result.TokenType:=ttIdentifier;
+end;
+
+function TWITScanner.HandleString : TToken;
+
+var
+  lTokenValue : string;
+begin
+  lTokenValue:='';
+  Result:=GetNewToken(ttStringLiteral);
+  if not GetNextChar then
+    FCurrentChar := #0;
+
+  while (FCurrentChar <> '"') and (FCurrentChar <> #0) do
+    begin
+    lTokenValue := lTokenValue + FCurrentChar;
+    if not GetNextChar then
+      FCurrentChar := #0;
+    end;
+
+  if FCurrentChar = '"' then
+    If not GetNextChar then
+      FCurrentChar:=#0;
+  Result.Value := lTokenValue;
+end;
+
+function TWITScanner.GetTokenInternal: TToken;
+var
+  lToken: TTokenType;
+begin
+  if FCurrentChar = #0 then
+    Result:=GetNewToken(ttEOF)
+  else if IsWhitespace(FCurrentChar) then
+    Result:=HandleWhitespace
+  else if IsIdentifierStart(FCurrentChar) then
+    Result:=HandleIdentifier
+  else if IsDigit(FCurrentChar) then
+    Result:=HandleNumber
+  else if FCurrentChar = '"' then
+    Result:=HandleString
+  else if IsSymbol(FCurrentChar,lToken) then
+    begin
+    if (lToken=ttDiv) and (PeekNextChar in ['/','*']) then
+      Result:=HandleComment
+    else
+      begin
+      Result:=GetNewToken(lToken);
+      Result.Value:=FCurrentChar;
+      if not getNextChar then
+        FCurrentChar:=#0;
+      end;
+    end
+  else
+    begin
+    Result:=GetNewToken(ttUnknown);
+    Result.Value := FCurrentChar;
+    if not getNextChar then
+      FCurrentChar:=#0;
+    end;
+end;
+
+function TWITScanner.GetToken: TToken;
+var
+  lToken : TToken;
+begin
+  Repeat
+    lToken:=GetTokenInternal;
+  until (Not (lToken.TokenType in [ttWhitespace,ttComment])) or Not SkipWhiteSpace;
+  Result:=lToken;
+end;
+
+{ TTokenTypeHelper }
+
+function TTokenTypeHelper.tostring: string;
+begin
+  if (self>=low(TKeyWordType)) and (self<=high(TKeyWordType)) then
+    Result:=Keywords[Self]
+  else if (self>=low(TSymbolType)) and (self<=high(TSymbolType)) then
+    Result:=Symbols[Self]
+  else
+    case self of
+      ttIdentifier : Result:='identifier';
+      ttWhitespace : Result:='whitespace';
+    else
+      Result:='<unknown>';
+    end;
+end;
+
+{ TTokenTypesHelper }
+
+function TTokenTypesHelper.tostring: string;
+var
+  T : TTokenType;
+begin
+  Result:='';
+  For T in Self do
+    begin
+    if Result<>'' then
+      Result:=Result+',';
+    Result:=Result+T.ToString;
+    end;
+end;
+
+end.

+ 6 - 0
packages/fcl-wit/tests/README.md

@@ -0,0 +1,6 @@
+# FCL - WIT tests
+
+Here the WIT classes are tested. You can run the official WIT specification
+tests by specifying the -d DIRECTORY  command-line option: for each .wit
+file in that directory the parser will be invoked. The .wit files are
+assumed to be correct WIT documents.

+ 102 - 0
packages/fcl-wit/tests/testwit.lpi

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <SaveOnlyProjectUnits Value="True"/>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="WIT document handling test data"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <CustomData Count="3">
+      <Item0 Name="OpenAPIBase"/>
+      <Item1 Name="OpenAPIConfig"/>
+      <Item2 Name="OpenAPIFile"/>
+    </CustomData>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="testwit.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="../src/wit.model.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="WIT.Model"/>
+      </Unit>
+      <Unit>
+        <Filename Value="../src/wit.scanner.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="WIT.Scanner"/>
+      </Unit>
+      <Unit>
+        <Filename Value="../src/wit.parser.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="WIT.Parser"/>
+      </Unit>
+      <Unit>
+        <Filename Value="utcwitscanner.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="utcwitparser.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="tcwitparser"/>
+      </Unit>
+      <Unit>
+        <Filename Value="utcwitmodel.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="tcwitmodel"/>
+      </Unit>
+      <Unit>
+        <Filename Value="utcrundirtests.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="testwit"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../src"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <Linking>
+      <Debugging>
+        <DebugInfoType Value="dsDwarf3"/>
+        <UseHeaptrc Value="True"/>
+      </Debugging>
+    </Linking>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 44 - 0
packages/fcl-wit/tests/testwit.lpr

@@ -0,0 +1,44 @@
+program testwit;
+
+uses 
+  WIT.Model, WIT.Scanner, WIT.Parser, 
+  utcwitscanner, utcwitparser, utcwitmodel, utcrundirtests,
+  consoletestrunner,  testregistry;
+
+type
+
+  { TTestRunner }
+
+  TTestRunner = class(consoletestrunner.TTestRunner)
+    function GetShortOpts: string; override;
+    procedure AppendLongOpts; override;
+  end;
+
+var
+  Application : TTestRunner;
+
+{ TTestRunner }
+
+function TTestRunner.GetShortOpts: string;
+begin
+  Result:='d'+inherited GetShortOpts;
+end;
+
+procedure TTestRunner.AppendLongOpts;
+begin
+  Inherited;
+  LongOpts.Add('test-dir');
+end;
+
+begin
+  DefaultFormat:=fPlain;
+  DefaultRunAllTests:=True;
+  Application:=TTestRunner.Create(Nil);
+  Application.Initialize;
+  if Application.HasOption('d','test-dir') then
+    GetTestRegistry.AddTest(TDirectoryFileTests.CreateFromDir(Application.GetOptionValue('d','test-dir')));
+  Application.Run;
+  Application.Free;
+
+end.
+

+ 166 - 0
packages/fcl-wit/tests/utcrundirtests.pp

@@ -0,0 +1,166 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2025 Michael Van Canneyt ([email protected])
+
+    Run wasm consortium WIT testsuite tests
+
+    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.
+
+ **********************************************************************}
+
+unit utcrundirtests;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes,
+  SysUtils,
+  fpcunit,
+  WIT.Model,
+  WIT.Scanner,
+  WIT.Parser;
+
+
+type
+  { TSchemaFileTest }
+
+  { TFileTest }
+
+  TFileTest = class(TAssert)
+  Private
+    FIgnores: Boolean;
+    FFileName : String;
+  protected
+    function GetTestName: string; override;
+    function GetTestSuiteName: string; override;
+    function GetEnableIgnores: boolean; override;
+    procedure SetEnableIgnores(Value: boolean); override;
+    procedure SetTestSuiteName(const aName: string); override;
+    procedure TestFile; virtual;
+  Public
+    constructor Create(const aFileName: String);
+    function CountTestCases: integer; override;
+    procedure Run(AResult: TTestResult); override;
+    property TestFileName : String Read FFileName;
+  end;
+
+  { TDirectoryFileTests }
+  TDirectoryFileTests = class(TTestSuite)
+  public
+    Constructor CreateFromDir(const aDir : String);
+  end;
+
+implementation
+
+function TFileTest.GetTestName: string;
+begin
+  Result:=ChangeFileExt(ExtractFileName(FFileName),'')
+end;
+
+function TFileTest.GetTestSuiteName: string;
+begin
+  Result:='';
+end;
+
+function TFileTest.GetEnableIgnores: boolean;
+begin
+  Result:=FIgnores;
+end;
+
+procedure TFileTest.SetEnableIgnores(Value: boolean);
+begin
+  FIgnores:=Value;
+end;
+
+procedure TFileTest.SetTestSuiteName(const aName: string);
+begin
+  // Nothing
+end;
+
+procedure TFileTest.TestFile;
+var
+  lScanner : TWITScanner;
+  lParser : TWITParser;
+  lStream : TStream;
+  lDocument : TWITDocument;
+  lErr : string;
+begin
+  lScanner:=Nil;
+  lParser:=Nil;
+  lDocument:=Nil;
+  lStream:=TFileStream.Create(FFileName,fmOpenRead or fmShareDenyWrite);
+  try
+    lScanner:=TWITScanner.Create(lStream);
+    lParser:=TWITParser.Create(lScanner,True);
+    try
+      lScanner:=Nil;
+      LDocument:=LParser.ParseDocument;
+    except
+      on E : exception do
+        lErr:=E.ClassName+' : '+E.Message;;
+    end;
+    if lErr<>'' then
+      Fail('Failed to parse '+TestFileName+' : '+lErr);
+  finally
+    LDocument.Free;
+    lScanner.Free;
+    lParser.Free;
+    lStream.Free;
+ end;
+
+end;
+
+constructor TFileTest.Create(const aFileName: String);
+begin
+  FFileName:=aFileName;
+end;
+
+function TFileTest.CountTestCases: integer;
+begin
+  Result:=1;
+end;
+
+
+Procedure DoRun(aTest: TTest; aResult: TTestResult);
+
+begin
+  TFileTest(aTest).TestFile;
+end;
+
+procedure TFileTest.Run(AResult: TTestResult);
+begin
+  aResult.StartTest(Self);
+  FLastStep:=TTestStep.stRunTest;
+  AResult.RunProtected(Self,@DoRun);
+  aResult.EndTest(Self);
+end;
+
+{ TDirectoryFileTests }
+
+constructor TDirectoryFileTests.CreateFromDir(const aDir: String);
+var
+  lInfo : TSearchRec;
+  lDir : String;
+begin
+  inherited create('TestDir');
+  lDir:=IncludeTrailingPathDelimiter(aDir);
+  if FindFirst(lDir+'*.wit',0,lInfo)=0 then
+    try
+      Repeat
+        AddTest(TFileTest.Create(lDir+lInfo.Name));
+      until FindNext(lInfo)<>0;
+    finally
+      FindClose(lInfo);
+    end;
+end;
+
+
+end.
+

+ 509 - 0
packages/fcl-wit/tests/utcwitmodel.pp

@@ -0,0 +1,509 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2025 Michael Van Canneyt ([email protected])
+
+    Test WIT model classes - mainly .ToString functionality.
+
+    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.
+
+ **********************************************************************}
+
+unit utcwitmodel;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  fpcunit, testregistry, Classes, SysUtils, WIT.Model;
+
+Type
+
+  { TTestWITModel }
+
+  TTestWITModel = class (TTestCase)
+  private
+    FEl: TWITBaseElement;
+  Public
+    Procedure SetUp; override;
+    Procedure TearDown; override;
+    // This will be freed at the end...
+    property Element : TWITBaseElement Read FEl Write FEl;
+  Published
+    Procedure TestPackageToString;
+    procedure TestUseToString;
+    procedure TestFunctionTypeToString;
+    procedure TestFunctionToString;
+    Procedure TestWITTypeToString;
+    Procedure TestTypeDefToString;
+    Procedure TestListTypeToString;
+    Procedure TestTupleTypeToString;
+    Procedure TestOptionTypeToString;
+    Procedure TestResultTypeToString;
+    Procedure TestHandleTypeToString;
+    Procedure TestFutureTypeToString;
+    Procedure TestStreamTypeToString;
+    procedure TestRecordFieldToString;
+    Procedure TestRecordToString;
+    Procedure TestEnumToString;
+    Procedure TestFlagsToString;
+    Procedure TestVariantToString;
+    Procedure TestResourceToString;
+    Procedure TestInterfaceToString;
+    procedure TestExchangeIdentifier;
+    Procedure TestIncludeToString;
+    Procedure TestWorldToString;
+  end;
+
+implementation
+
+{ TTestWITModel }
+
+procedure TTestWITModel.SetUp;
+begin
+  Inherited;
+  FreeAndNil(FEl);
+end;
+
+procedure TTestWITModel.TearDown;
+begin
+  FreeAndNil(FEl);
+  Inherited;
+end;
+
+procedure TTestWITModel.TestPackageToString;
+
+var
+  lPackage : TWITPackage;
+
+begin
+  lPackage:=TWITPackage.Create;
+  Element:=lPackage;
+  lPackage.PackageName:='a';
+  AssertEquals('Name','package a;',Element.ToString);
+  lPackage.Namespace:='b';
+  AssertEquals('NameSpace+Name','package b:a;',Element.ToString);
+  lPackage.Version:='1.1.1';
+  AssertEquals('NameSpace+Name+version','package b:[email protected];',Element.ToString);
+  lPackage.Worlds.Add(TWITWorld.Create('d'));
+  AssertEquals('world','package b:[email protected];'+sLineBreak+'world d {}'+sLineBreak,Element.ToString);
+  lPackage.IsNested:=True;
+  AssertEquals('world nested','package b:[email protected] {'+sLineBreak+'world d {}'+sLineBreak+'}'+sLineBreak,Element.ToString);
+  lPackage.Worlds.Clear;
+  AssertEquals('nested','package b:[email protected] {'+sLineBreak+'}'+sLineBreak,Element.ToString);
+end;
+
+procedure TTestWITModel.TestUseToString;
+var
+  lUse : TWITTopLevelUse;
+begin
+  lUse:=TWITTopLevelUse.Create;
+  Element:=lUse;
+  lUse.Path.PackageName:='a';
+  AssertEquals('Name','use a;',Element.ToString);
+  lUse.Path.Version:='1.1.1';
+  AssertEquals('Name@ver','use [email protected];',Element.ToString);
+  lUse.Path.Namespaces.Add('b');
+  AssertEquals('Namespace:Name@ver','use b:[email protected];',Element.ToString);
+  lUse.Path.Namespaces.Add('c');
+  AssertEquals('double namespace','use b:c:[email protected];',Element.ToString);
+  lUse.Rename:='d';
+  AssertEquals('alias','use b:c:[email protected] as d;',Element.ToString);
+end;
+
+procedure TTestWITModel.TestInterfaceToString;
+
+var
+  lIntf : TWITInterface;
+  lUse : TWITUse;
+
+begin
+  lIntf:=TWITInterface.Create('a');
+  Element:=lIntf;
+  AssertEquals('Name','interface a {}',Element.ToString);
+  lIntf.AddType(TWITTypeDef.Create('b',TWitType.Create(wtu8)));
+  AssertEquals('Type',  'interface a {'+sLinebreak
+                                    +'  type b = u8;'+sLinebreak
+                                    +'}',Element.ToString);
+  lIntf.AddType(TWITTypeDef.Create('c',TWitType.Create(wtu32)));
+  AssertEquals('Two Types',  'interface a {'+sLinebreak
+                                    +'  type b = u8;'+sLinebreak
+                                    +'  type c = u32;'+sLinebreak
+                                    +'}',Element.ToString);
+  lIntf.AddFunction(TWITFunction.Create('d',TWitFunctionType.Create));
+  AssertEquals('Type & func',  'interface a {'+sLinebreak
+                                    +'  type b = u8;'+sLinebreak
+                                    +'  type c = u32;'+sLinebreak
+                                    +'  d : func();'+sLinebreak
+                                    +'}',Element.ToString);
+  lUse:=TWITUse.Create;
+  lUse.Path.PackageName:='e';
+  LUse.AddItem('f');
+  lIntf.AddUses(lUse);
+  AssertEquals('Use, types and func',  'interface a {'+sLinebreak
+                                    +'  use e.{f};'+sLinebreak
+                                    +'  type b = u8;'+sLinebreak
+                                    +'  type c = u32;'+sLinebreak
+                                    +'  d : func();'+sLinebreak
+                                    +'}',Element.ToString);
+
+end;
+
+procedure TTestWITModel.TestExchangeIdentifier;
+
+var
+  lEx : TWITExchangeIdentifier;
+
+begin
+  lEx:=TWITExchangeIdentifier.Create(xtImport,'a');
+  Element:=lEx;
+  AssertEquals('Import simple path','import a;',Element.ToString);
+  lEx.Free;
+  lEx:=TWITExchangeIdentifier.Create(xtExport,'a');
+  Element:=lEx;
+  AssertEquals('Export simple path','export a;',Element.ToString)
+end;
+
+procedure TTestWITModel.TestIncludeToString;
+var
+  lInc : TWITInclude;
+  lItm : TWitIncludeItem;
+
+begin
+  lInc:=TWITInclude.Create;
+  linc.Path.Identifier:='a';
+  Element:=lInc;
+  AssertEquals('Include simple path','include a;',Element.ToString);
+  LItm:=TWitIncludeItem.Create('b');
+  lInc.Items.Add(LItm);
+  AssertEquals('Include item path','include a with {b}',Element.ToString)
+end;
+
+
+procedure TTestWITModel.TestWorldToString;
+var
+  lWorld : TWITWorld;
+  lUse : TWITUse;
+  lInclude : TWITInclude;
+
+begin
+  lWorld:=TWITWorld.Create('a');
+  Element:=lWorld;
+  AssertEquals('Name','world a {}',Element.ToString);
+  lWorld.AddImport(TWITExchangeIdentifier.Create(xtImport,'b'));
+  AssertEquals('Import',  'world a {'+sLinebreak
+                                    +'  import b;'+sLinebreak
+                                    +'}',Element.ToString);
+  lWorld.AddExport(TWITExchangeIdentifier.Create(xtExport,'c'));
+  AssertEquals('Import/export',  'world a {'+sLinebreak
+                                    +'  import b;'+sLinebreak
+                                    +'  export c;'+sLinebreak
+                                    +'}',Element.ToString);
+  lWorld.AddTypeDef(TWITTypeDef.Create('d',TWitType.Create(wtU8)));
+  AssertEquals('Type, Import, Export',  'world a {'+sLinebreak
+                                    +'  type d = u8;'+sLinebreak
+                                    +'  import b;'+sLinebreak
+                                    +'  export c;'+sLinebreak
+                                    +'}',Element.ToString);
+  lUse:=TWITUse.Create;
+  lUse.Path.PackageName:='e';
+  lUse.AddItem('f','g');
+  lWorld.AddUses(lUse);
+  AssertEquals('Use, Type, Import, Export',  'world a {'+sLinebreak
+                                    +'  use e.{f as g};'+sLinebreak
+                                    +'  type d = u8;'+sLinebreak
+                                    +'  import b;'+sLinebreak
+                                    +'  export c;'+sLinebreak
+                                    +'}',Element.ToString);
+  lInclude:=TWITInclude.Create;
+  lInclude.Path.PackageName:='h';
+  lWorld.AddINclude(lInclude);
+  AssertEquals('include, Use, Type, Import, Export',  'world a {'+sLinebreak
+                                    +'  include h;'+sLinebreak
+                                    +'  use e.{f as g};'+sLinebreak
+                                    +'  type d = u8;'+sLinebreak
+                                    +'  import b;'+sLinebreak
+                                    +'  export c;'+sLinebreak
+                                    +'}',Element.ToString);
+
+end;
+
+
+procedure TTestWITModel.TestFunctionTypeToString;
+
+var
+  lFunc : TWITFunctionType;
+
+begin
+  lFunc:=TWITFunctionType.Create;
+  Element:=LFunc;
+  AssertEquals('Empty','func()',Element.ToString);
+  lFunc.Flags:=[ffConstructor];
+  AssertEquals('constructor','constructor()',Element.ToString);
+  lFunc.Flags:=[ffAsync];
+  AssertEquals('Async','async func()',Element.ToString);
+  lFunc.Flags:=[ffstatic];
+  AssertEquals('Static','static func()',Element.ToString);
+  lFunc.Flags:=[];
+  lFunc.ResultType:=TWitType.Create(wts8);
+  AssertEquals('Res','func() -> s8',Element.ToString);
+  lFunc.Parameters.Add(TWitFuncParam.Create('b',TWITType.Create(wtu8)));
+  AssertEquals('Res','func(b: u8) -> s8',Element.ToString);
+  lFunc.Parameters.Add(TWitFuncParam.Create('c',TWITType.Create(wtu32)));
+  AssertEquals('Res','func(b: u8, c: u32) -> s8',Element.ToString);
+end;
+
+procedure TTestWITModel.TestFunctionToString;
+
+var
+  lFuncTyp : TWITFunctionType;
+  lFunc : TWITFunction;
+
+begin
+  lFuncTyp:=TWITFunctionType.Create;
+  lFunc:=TWITFunction.Create('a',lFuncTyp);
+  Element:=LFunc;
+  AssertEquals('Empty','a : func();',Element.ToString);
+end;
+
+procedure TTestWITModel.TestWITTypeToString;
+var
+  lType : TWITType;
+begin
+  lType:=TWITType.Create(wtu32);
+  Element:=lType;
+  AssertEquals('Type','u32',lType.ToString);
+end;
+
+procedure TTestWITModel.TestTypeDefToString;
+var
+  lType : TWITTypeDef;
+begin
+  lType:=TWITTypeDef.Create('a',TWITType.Create(wtu32));
+  Element:=lType;
+  AssertEquals('Type','type a = u32;',lType.ToString);
+end;
+
+procedure TTestWITModel.TestListTypeToString;
+var
+  lType : TWITListType;
+begin
+  lType:=TWITListType.Create(TWITType.Create(wtu32));
+  Element:=lType;
+  AssertEquals('List type','list<u32>',lType.ToString);
+  lType.ItemCount:=3;
+  AssertEquals('List type','list<u32,3>',lType.ToString);
+end;
+
+procedure TTestWITModel.TestTupleTypeToString;
+var
+  lType : TWITTupleType;
+begin
+  lType:=TWITTupleType.Create;
+  lType.AddItem(TWITType.Create(wtu32));
+  Element:=lType;
+  AssertEquals('Tuple type','tuple<u32>',lType.ToString);
+  lType.AddItem(TWITType.Create(wtu8));
+  AssertEquals('Tuple type 2','tuple<u32,u8>',lType.ToString);
+end;
+
+procedure TTestWITModel.TestOptionTypeToString;
+var
+  lType : TWITOptionType;
+begin
+  lType:=TWITOptionType.Create(TWITType.Create(wtu32));
+  Element:=lType;
+  AssertEquals('Option type','option<u32>',lType.ToString);
+end;
+
+procedure TTestWITModel.TestResultTypeToString;
+var
+  lType : TWITResultType;
+begin
+  lType:=TWITResultType.Create(TWITType.Create(wtu32),TWITType.Create(wtString));
+  Element:=lType;
+  AssertEquals('Result type','result<u32,string>',lType.ToString);
+end;
+
+procedure TTestWITModel.TestHandleTypeToString;
+var
+  lType : TWITHandleType;
+begin
+  lType:=TWITHandleType.Create('a',true);
+  Element:=lType;
+  AssertEquals('Handle type','borrow<a>',lType.ToString);
+  lType.Free;
+  lType:=TWITHandleType.Create('a',False);
+  Element:=lType;
+   AssertEquals('Handle type','own<a>',lType.ToString);
+end;
+
+procedure TTestWITModel.TestFutureTypeToString;
+var
+  lType : TWITFutureType;
+begin
+  lType:=TWITFutureType.Create(TWITType.Create(wtu32));
+  Element:=lType;
+  AssertEquals('future type','future<u32>',lType.ToString);
+end;
+
+procedure TTestWITModel.TestStreamTypeToString;
+var
+  lType : TWITStreamType;
+begin
+  lType:=TWITStreamType.Create(TWITType.Create(wtu32));
+  Element:=lType;
+  AssertEquals('Stream type','stream<u32>',lType.ToString);
+end;
+
+procedure TTestWITModel.TestRecordFieldToString;
+
+var
+  lField : TWITRecordField;
+
+begin
+  lField:=TWITRecordField.Create('fld',TWITType.Create(wtU64));
+  Element:=lField;
+  AssertEquals('Field','fld : u64',lField.ToString)
+end;
+
+procedure TTestWITModel.TestRecordToString;
+
+var
+  lRecord : TWITRecordType;
+
+begin
+  lRecord:=TWITRecordType.Create;
+  Element:=lRecord;
+  AssertEquals('Empty','record  {}',lRecord.ToString);
+  lRecord.AddField(TWITRecordField.Create('fld',TWITType.Create(wtU64)));
+  AssertEquals('One field','record  {'+sLineBreak
+                                    +'  fld : u64'+sLineBreak
+                                    +'}',lRecord.ToString);
+  lRecord.AddField(TWITRecordField.Create('fld2',TWITType.Create(wtString)));
+  AssertEquals('Two fields','record  {'+sLineBreak
+                           +'  fld : u64,'+sLineBreak
+                           +'  fld2 : string'+sLineBreak
+                           +'}',lRecord.ToString);
+  Element:=TWITTypeDef.Create('a',lRecord);
+  AssertEquals('Typedef','record a {'+sLineBreak
+                           +'  fld : u64,'+sLineBreak
+                           +'  fld2 : string'+sLineBreak
+                           +'}',Element.ToString);
+end;
+
+
+procedure TTestWITModel.TestEnumToString;
+var
+  lEnum : TWITEnumType;
+
+begin
+  lEnum:=TWITEnumType.Create;
+  Element:=lEnum;
+  AssertEquals('Empty','enum  {}',lEnum.ToString);
+  lEnum.AddCase('one');
+  AssertEquals('One case','enum  {'+sLineBreak
+                                    +'  one'+sLineBreak
+                                    +'}',lEnum.ToString);
+  lEnum.AddCase('two');
+  AssertEquals('Two cases','enum  {'+sLineBreak
+                           +'  one,'+sLineBreak
+                           +'  two'+sLineBreak
+                           +'}',lEnum.ToString);
+  Element:=TWITTypeDef.Create('a',lEnum);
+  AssertEquals('Typedef','enum a {'+sLineBreak
+                           +'  one,'+sLineBreak
+                           +'  two'+sLineBreak
+                           +'}',Element.ToString);
+end;
+
+procedure TTestWITModel.TestFlagsToString;
+var
+  lFlags : TWITFlagsType;
+
+begin
+  lFlags:=TWITFlagsType.Create;
+  Element:=lFlags;
+  AssertEquals('Empty','flags  {}',lFlags.ToString);
+  lFlags.AddFlag('one');
+  AssertEquals('One flag','flags  {'+sLineBreak
+                                    +'  one'+sLineBreak
+                                    +'}',lFlags.ToString);
+  lFlags.AddFlag('two');
+  AssertEquals('Two cases','flags  {'+sLineBreak
+                           +'  one,'+sLineBreak
+                           +'  two'+sLineBreak
+                           +'}',lFlags.ToString);
+  Element:=TWITTypeDef.Create('a',lFlags);
+  AssertEquals('Typedef','flags a {'+sLineBreak
+                           +'  one,'+sLineBreak
+                           +'  two'+sLineBreak
+                           +'}',Element.ToString);
+end;
+
+procedure TTestWITModel.TestVariantToString;
+var
+  lVar : TWITVariantType;
+
+begin
+  lVar:=TWITVariantType.Create;
+  Element:=lVar;
+  AssertEquals('Empty','variant  {}',Element.ToString);
+  lVar.AddCase(TWitVariantCase.Create('one'));
+  AssertEquals('One flag','variant  {'+sLineBreak
+                                    +'  one'+sLineBreak
+                                    +'}',Element.ToString);
+  lVar.AddCase(TWitVariantCase.Create('two'));
+  AssertEquals('Two cases','variant  {'+sLineBreak
+                           +'  one,'+sLineBreak
+                           +'  two'+sLineBreak
+                           +'}',Element.ToString);
+  lVar.AddCase(TWitVariantCase.Create('three',TWitType.Create(wtu8)));
+  AssertEquals('Two cases','variant  {'+sLineBreak
+                           +'  one,'+sLineBreak
+                           +'  two,'+sLineBreak
+                           +'  three(u8)'+sLineBreak
+                           +'}',Element.ToString);
+  Element:=TWITTypeDef.Create('a',lVar);
+  AssertEquals('Typedef','variant a {'+sLineBreak
+                           +'  one,'+sLineBreak
+                           +'  two,'+sLineBreak
+                           +'  three(u8)'+sLineBreak
+                           +'}',Element.ToString);
+end;
+
+procedure TTestWITModel.TestResourceToString;
+var
+  lRes : TWITResourceType;
+  lFunc : TWITFunction;
+  lFuncType : TWITFunctionType;
+begin
+  lRes:=TWITResourceType.Create('a');
+  Element:=lRes;
+  AssertEquals('empty','resource a;',Element.ToString);
+  lFuncType:=TWITFunctionType.Create;
+  lFuncType.Flags:=[ffConstructor];
+  lFunc:=TWITFunction.Create('constructor',lFuncType);
+  lRes.Functions.Add(lFunc);
+  AssertEquals('empty','resource a {'+sLineBreak
+                      +'  constructor();'+sLineBreak
+                      +'}',Element.ToString);
+  lFuncType:=TWITFunctionType.Create;
+  lFuncType.ResultType:=TWitType.Create(wtU8);
+  lFunc:=TWITFunction.Create('b',lFuncType);
+  lRes.Functions.Add(lFunc);
+  AssertEquals('empty','resource a {'+sLineBreak
+                      +'  constructor();'+sLineBreak
+                      +'  b : func() -> u8;'+sLineBreak
+                      +'}',Element.ToString);
+end;
+
+initialization
+  RegisterTest(TTestWITModel);
+end.
+

+ 1900 - 0
packages/fcl-wit/tests/utcwitparser.pp

@@ -0,0 +1,1900 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2025 Michael Van Canneyt ([email protected])
+
+    Test WIT parser
+
+    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.
+
+ **********************************************************************}
+unit utcwitparser;
+
+interface
+
+// uncomment this to show parsed WIT content }
+{ $DEFINE LOGPARSEDCONTENT}
+
+uses
+  fpcunit, testregistry, Classes, SysUtils,
+  WIT.Scanner, WIT.Model, WIT.Parser;
+
+
+type
+  TResultIgnore = (riResult,riError);
+  TResultIgnores = set of TResultIgnore;
+
+  { TTestWITParser }
+
+  TTestWITParser = class(TTestCase)
+  private
+    FScanner: TWITScanner;
+    FParser: TWITParser;
+    FDocument: TWITDocument;
+    FInputStream: TStringStream;
+  protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+    procedure InitParser(const aContent: string);
+    class function AssertListType(const aMsg: string; aTypeDef: TWitTypeDef;const aName: String; aElementKind: TWITTypeKind; aCount : Integer = 0): TWitType;
+    class function AssertEnumType(const aMsg: String; aType: TWITTypeDef;const aName: String; aValues: array of string) : TWITEnumType;
+    class function AssertResultType(const aMsg: string; aType: TWITTypeDef;const aName: String; aOKKind: TWITTypeKind; aErrorKind: TWitTypeKind; aIgnore: TResultIgnores) : TWITResultType;
+    class function AssertOptionType(const aMsg: string; aType: TWITTypeDef;const aName: String; aOptionKind: TWITTypeKind) : TWITOptionType;
+    class function AssertStreamType(const aMsg: string; aType: TWITTypeDef;const aName: String; aStreamKind: TWITTypeKind): TWITStreamType;
+    class function AssertFutureType(const aMsg: string; aType: TWITTypeDef;const aName: String; aFutureKind: TWITTypeKind): TWITFutureType;
+    class function AssertTupleType(const aMsg: string; aType: TWITTypeDef;const aName: String; aOptionKind: Array of TWITTypeKind) : TWITTupleType;
+    class function AssertFlagsType(const aMsg: string; aType: TWITTypeDef;const aName: String; aFlagNames: Array of String) : TWITFlagsType;
+    class function AssertVariantType(const aMsg: string; aType: TWITTypeDef;const aName: String; aVariantNames: array of String): TWITVariantType;
+    class function AssertRecordType(const aMsg: string; aType: TWITTypeDef;const aName: String; aFieldNames: array of String; aFieldTypes : Array of TWITTypeKind): TWITRecordType;
+    class function AssertResourceType(const aMsg: string; aType: TWITTypeDef;const aName: String; aConstructor: boolean; aFunctionNames: array of String): TWITResourceType;
+    class function AssertAliasType(const aMsg: string; aType: TWITTypeDef;const aName: String; aAliasName:String): TWITIdentifierType;
+    class function AssertHandleType(const aMsg: string; aType: TWITTypeDef;const aName: String; aAliasName:String): TWITHandleType;
+    class function AssertTypeDef(const Msg: String; aType: TWITType; aExpectedName: string; aExpectedUnderlyingKind: TWITTypeKind) : TWITType;
+    class procedure AssertAnnotationArgument(const Msg: String; aArgument: TWITAnnotationArgument; aName, aValue: string);
+    class procedure AssertAnnotation(const Msg: String; aAnnotation: TWITAnnotation;const aName: String; Args: array of String);
+    class procedure AssertInclude(const aMsg: string; aInclude: TWITInclude;const aName: String; aItemNames, aItemAliases: array of String);
+    class procedure AssertFunction(const Msg: String; aFunc: TWITFunction;const aName: String; aArgCount: Integer; aHaveResult: Boolean; aAnnotationCount: Integer = 0);
+    class procedure AssertFunctionParam(const Msg: String; aParam: TWITFuncParam;const aName: String; aTypeKind: TWITTypeKind; aTypeName: string);
+    class procedure AssertInterface(const Msg: String; aIntf: TWITInterface;const aName: String; aFuncCount: Integer; aTypeCount : Integer; aAnnotationCount: Integer = 0);
+    class procedure AssertWorld(const Msg : String; aWorld :TWITWorld; const aWorldName : String; aExportCount,aImportCount,aUseCount,aTypeDefCount,aIncludeCount : Integer);
+    class procedure AssertEquals(Msg: string; aExpected, aActual: TWitTypeKind); overload;
+    class procedure AssertUse(Msg: string; aUse : TWITTopLevelUse; const aPackageName, aVersion, aIdentifier, aRename: string; aNameSpaces : Array of string);
+    class procedure AssertUsePath(Msg: string; aUse: TWITUsePath; const aPackageName, aVersion, aIdentifier: string; aNameSpaces: array of string);
+    class procedure AssertPackage(const Msg: String; aPackage: TWITPackage; aExpectedNamespace: string; aExpectedPackageName: string;
+      aExpectedVersion: string; aExpectedWorldCount: Integer=0; aExpectedImportCount: Integer=0; aExpectedExportCount: Integer=0;
+      aExpectedUseCount: Integer=0; aExpectedInterfaceCount: Integer=0);
+  Protected
+    function ParseWorld(const aWorldName : String; aExportCount,aImportCount,aUseCount,aTypeDefCount,aIncludeCount : Integer) : TWITWorld;
+    function ParseFunc(const aFuncName: String; aArgNames : Array of string; aArgTypes : Array of TWitTypeKind; aResultType : TWitTypeKind = wtVoid): TWITFunction;
+    function ParseType(const aInterFaceName, aTypeName: String): TWITTypeDef;
+    function ParseInterface(const aName: String; aFunctionCount, aTypeCount, aAnnotationCount: integer): TWITInterface;
+    function WrapTypeDef(const aDef: String; isType : Boolean = false): string;
+    function WrapFunc(const aParams: String; aResult : string = ''): string;
+  published
+    procedure TestParsePackageEmpty;
+    procedure TestParseExitInterfaceDocument;
+    procedure TestParsePackageVersions;
+    procedure TestSimpleTypes;
+    procedure TestListType;
+    procedure TestListListType;
+    procedure TestListSized;
+    procedure TestListSizedListSized;
+    procedure TestEnum;
+    procedure TestEnumEndWithComma;
+    procedure TestResultEmpty;
+    procedure TestResultOneType;
+    procedure TestResultTwoTypes;
+    procedure TestResultOneIgnoredTyoe;
+    procedure TestOption;
+    procedure TestStream;
+    procedure TestStreamEmpty;
+    procedure TestFuture;
+    procedure TestNestedFuture;
+    procedure TestFutureEmpty;
+    procedure TestTupleEmpty;
+    procedure TestTuple1;
+    procedure TestTuple2;
+    procedure TestTuple3;
+    procedure TestTupleComma;
+    procedure TestFlagsEmpty;
+    procedure TestFlags1;
+    procedure TestFlags2;
+    procedure TestFlags3;
+    procedure TestFlagsComma;
+    procedure TestVariant1;
+    procedure TestVariant2;
+    procedure TestVariant2Comma;
+    procedure TestVariantTypedSimple;
+    procedure TestVariantTypedSimpleComma;
+    procedure TestVariantTypedComplex;
+    procedure TestRecordEmpty;
+    procedure TestRecord1;
+    procedure TestRecord2;
+    procedure TestRecord2Comma;
+    procedure TestRecordRecordName;
+    procedure TestAlias;
+    procedure TestBorrowedHandle;
+    procedure TestResourceEmpty;
+    procedure TestResourceEmpty2;
+    procedure TestResourceConstructor;
+    procedure TestResourceOneMethod;
+    procedure TestResourceStaticMethod;
+    procedure TestResourceAsyncMethod;
+    procedure TestResourceTwoMethods;
+    procedure TestResourceOneMethodAndConstructor;
+    procedure TestUseIdentifier;
+    procedure TestUseIdentifierAs;
+    procedure TestUseFullIdentifier;
+    procedure TestUseFullIdentifierVersion;
+    procedure TestUseFullIdentifierAs;
+    procedure TestUseFullIdentifierVersionAs;
+    procedure TestParseFunctionEmpty;
+    procedure TestParseFunctionEmptyResult;
+    procedure TestParseFunctionOneParam;
+    procedure TestParseFunctionOneParamResult;
+    procedure TestParseFunctionTwoParams;
+    procedure TestParseFunctionTwoParamsResult;
+    procedure TestParseWorldEmpty;
+    procedure TestParseWorldUse;
+    procedure TestParseWorldUseAnnotation;
+    procedure TestParseWorldExport;
+    procedure TestParseWorldExportUse;
+    procedure TestParseWorldExportFunction;
+    procedure TestParseWorldExportInterface;
+    procedure TestParseWorldImport;
+    procedure TestParseWorldImportUse;
+    procedure TestParseWorldImportFunction;
+    procedure TestParseWorldImportInterface;
+    procedure TestParseWorldInclude;
+    procedure TestParseWorldIncludeUse;
+    procedure TestParseWorldIncludeUseList;
+    procedure TestParseWorldIncludeUseList2;
+    procedure TestParseWorldTypeDef;
+    procedure TestParseWorldEnumType;
+    procedure TestParseWorldVariantType;
+    procedure TestParseWorldRecordType;
+    procedure TestParseWorldFlagsType;
+    procedure TestParseInterfaceUse;
+    procedure TestParseInterfaceUseGate;
+  end;
+
+
+implementation
+
+uses TypInfo;
+
+
+{ TTestWITParser }
+
+procedure TTestWITParser.InitParser(const aContent : string);
+begin
+  FreeAndNil(FDocument);
+  FreeAndNil(FInputStream);
+  {$IFDEF LOGPARSEDCONTENT}
+  Writeln(TestName,' - Parsing:');
+  Writeln(aContent);
+  {$ENDIF}
+
+  FInputStream := TStringStream.Create(aContent);
+  // Assuming TWITScanner.Create(AStream: TStream);
+  // If TWITScanner has a different constructor (e.g., taking ownership of stream), adjust accordingly.
+  FreeAndNil(FScanner);
+  FScanner := TWITScanner.Create(FInputStream);
+  FreeAndNil(FParser);
+  FParser := TWITParser.Create(FScanner);
+end;
+
+procedure TTestWITParser.SetUp;
+begin
+  inherited SetUp;
+  FreeAndNil(FDocument);   // Freeing nil is safe
+  FreeAndNil(FParser);
+  FreeAndNil(FScanner);    // Assuming scanner does not own the stream, or handles it.
+  FreeAndNil(FInputStream);
+end;
+
+procedure TTestWITParser.TearDown;
+begin
+  FreeAndNil(FDocument);   // Freeing nil is safe
+  FreeAndNil(FParser);
+  FreeAndNil(FScanner);    // Assuming scanner does not own the stream, or handles it.
+  FreeAndNil(FInputStream);
+  inherited TearDown;
+end;
+
+function TTestWITParser.WrapTypeDef(const aDef: String; isType: Boolean): string;
+
+const
+  WIT_CONTENT =
+    'interface types {' + sLineBreak +
+    '  %s' + sLineBreak +
+    '}';
+
+begin
+  Result:=aDef;
+  if isType then
+    Result:='type a = '+Result+';';
+  Result:=Format(WIT_CONTENT,[Result]);
+end;
+
+function TTestWITParser.WrapFunc(const aParams: String; aResult: string): string;
+const
+  WIT_CONTENT =
+    'interface funcs {' + sLineBreak +
+    '  %s' + sLineBreak +
+    '}';
+var
+  lWIT : String;
+begin
+  lWIT:='a : func ('+aParams+')';
+  if (aResult<>'') then
+    lWIT:=lWIT+' -> '+aResult;
+  lWIT:=lWIT+';';
+  Result:=Format(WIT_CONTENT,[lWIT]);
+end;
+
+class procedure TTestWITParser.AssertEquals(Msg: string; aExpected, aActual: TWitTypeKind);
+begin
+  AssertEquals(Msg,GetEnumName(TypeInfo(TWitTypeKind),ord(aExpected)),
+                   GetEnumName(TypeInfo(TWitTypeKind),ord(aActual)));
+end;
+
+class procedure TTestWITParser.AssertUsePath(Msg: string; aUse: TWITUsePath; const aPackageName, aVersion, aIdentifier: string;
+  aNameSpaces: array of string);
+var
+  I : Integer;
+begin
+  AssertEquals(Msg+': PackageName',aPackageName,aUse.PackageName);
+  AssertEquals(Msg+': Version',aVersion,aUse.Version);
+  AssertEquals(Msg+': Identifier',aIdentifier,aUse.Identifier);
+  AssertEquals(Msg+': Namespace count',Length(aNameSpaces),aUse.Namespaces.Count);
+  For I:=0 to Length(aNamespaces)-1 do
+    AssertEquals(Msg+Format(': namespace[%d]',[i]),aNameSpaces[i],aUse.Namespaces[i]);
+end;
+
+class procedure TTestWITParser.AssertUse(Msg: string; aUse: TWITTopLevelUse; const aPackageName, aVersion, aIdentifier, aRename: string;
+  aNameSpaces: array of string);
+
+begin
+  AssertNotNull(Msg+': Have use',aUse);
+  AssertUsePath(Msg+': Path',aUse.Path,aPackageName,aVersion,aIdentifier,aNameSpaces);
+  AssertEquals(Msg+': Rename',aRename,aUse.Rename);
+end;
+
+class procedure TTestWITParser.AssertAnnotationArgument(const Msg : String; aArgument : TWITAnnotationArgument; aName,aValue : string);
+
+begin
+  AssertNotNull(Msg+': Have argument',aArgument);
+  AssertEquals(Msg+': name',aName,aArgument.Member);
+  AssertEquals(Msg+': value',aValue,aArgument.Value);
+end;
+
+class procedure TTestWITParser.AssertAnnotation(const Msg: String; aAnnotation: TWITAnnotation;const aName: String;
+  Args: array of String);
+var
+  I : Integer;
+begin
+  AssertNotNull(Msg+': Have annotation',aAnnotation);
+  AssertEquals(Msg+': name',aName,aAnnotation.Name);
+  AssertEquals(Msg+': Arg count',aAnnotation.Arguments.Count,Length(Args) div 2);
+  I:=0;
+  While (I<Length(Args)) do
+   begin
+   AssertAnnotationArgument(Msg+Format('Annotation[%d]',[i]),aAnnotation.Arguments[I div 2],Args[i],Args[i+1]);
+   Inc(I,2);
+   end;
+end;
+
+class procedure TTestWITParser.AssertInclude(const aMsg: string; aInclude: TWITInclude; const aName: String; aItemNames,
+  aItemAliases: array of String);
+begin
+  AssertNotNull(aMsg+': have include',aInclude);
+  AssertNotNull(aMsg+': have include path',aInclude.Path);
+  AssertNotNull(aMsg+': items',aInclude.Items);
+  AssertEquals(aMsg+': have path',aName,aInclude.Path.ToString);
+  AssertEquals(aMsg+': item count',Length(aItemNames),aInclude.Items.Count);
+end;
+
+class procedure TTestWITParser.AssertFunction(const Msg: String; aFunc: TWITFunction; const aName: String; aArgCount: Integer;
+  aHaveResult: Boolean; aAnnotationCount: Integer);
+
+begin
+  AssertNotNull(Msg+': Have function',aFunc);
+  AssertEquals(Msg+': name',aName,aFunc.Name);
+  AssertNotNull(Msg+': Type',aFunc.TypeDef);
+  AssertEquals(Msg+': Argument count',aArgCount,aFunc.TypeDef.Parameters.Count);
+  AssertEquals(Msg+': Annotation count',aArgCount,aFunc.Annotations.Count);
+  if aHaveResult then
+    AssertNotNull(Msg+': Have Result',aFunc.TypeDef.ResultType)
+  else
+    AssertNull(Msg+': Have no Result',aFunc.TypeDef.ResultType);
+end;
+
+class procedure TTestWITParser.AssertFunctionParam(const Msg: String; aParam: TWITFuncParam;const aName: String;
+  aTypeKind: TWITTypeKind; aTypeName: string);
+begin
+  AssertNotNull(Msg+': Have param',aParam);
+  AssertEquals(Msg+': param name',aName,aParam.Name);
+  AssertEquals(Msg+': param Type kind',aTypeKind,aParam.ParamType.Kind);
+end;
+
+class procedure TTestWITParser.AssertInterface(const Msg: String; aIntf: TWITInterface;const aName: String;
+  aFuncCount: Integer; aTypeCount: Integer; aAnnotationCount: Integer);
+begin
+  AssertNotNull(Msg+': Have Interface',aIntf);
+  AssertEquals(Msg+': name',aName,aIntf.Name);
+  AssertEquals(Msg+': function count',aFuncCount,aIntf.Functions.Count);
+  AssertEquals(Msg+': Type count',aTypeCount,aIntf.Types.Count);
+  AssertEquals(Msg+': Annotation count',aAnnotationCount,aIntf.Annotations.Count);
+end;
+
+class procedure TTestWITParser.AssertWorld(const Msg: String; aWorld: TWITWorld; const aWorldName: String; aExportCount,
+  aImportCount, aUseCount, aTypeDefCount, aIncludeCount: Integer);
+begin
+  AssertNotNull(Msg+': Have world',aWorld);
+  AssertEquals(Msg+': name',aWorldName,aWorld.Name);
+  AssertEquals(Msg+': export count',aExportCount,aWorld.Exported.Count);
+  AssertEquals(Msg+': import count',aImportCount,aWorld.Imported.Count);
+  AssertEquals(Msg+': use count',aUseCount,aWorld.UsesList.Count);
+  AssertEquals(Msg+': type count',aTypeDefCount,aWorld.TypeDefs.Count);
+  AssertEquals(Msg+': include count',aIncludeCount,aWorld.Includes.Count);
+end;
+
+class procedure TTestWITParser.AssertPackage(
+    const Msg: String;
+    aPackage: TWITPackage;
+    aExpectedNamespace: string;
+    aExpectedPackageName: string;
+    aExpectedVersion: string;
+    aExpectedWorldCount: Integer = 0;
+    aExpectedImportCount: Integer = 0;
+    aExpectedExportCount: Integer = 0;
+    aExpectedUseCount: Integer = 0;
+    aExpectedInterfaceCount: Integer = 0
+  );
+begin
+  AssertNotNull(Msg + ': Package object should exist', aPackage);
+  AssertEquals(Msg + ': Namespace', aExpectedNamespace, aPackage.Namespace);
+  AssertEquals(Msg + ': PackageName', aExpectedPackageName, aPackage.PackageName);
+  AssertEquals(Msg + ': Version', aExpectedVersion, aPackage.Version);
+
+  // Check that list objects themselves are created (common practice in constructors)
+  AssertNotNull(Msg + ': ImportList object should exist', aPackage.ImportList);
+  AssertEquals(Msg + ': ImportList count', aExpectedImportCount, aPackage.ImportList.Count);
+
+  AssertNotNull(Msg + ': ExportList object should exist', aPackage.ExportList);
+  AssertEquals(Msg + ': ExportList count', aExpectedExportCount, aPackage.ExportList.Count);
+
+  AssertNotNull(Msg + ': UseStatements object should exist', aPackage.UseStatements);
+  AssertEquals(Msg + ': UseStatements count', aExpectedUseCount, aPackage.UseStatements.Count);
+
+  AssertNotNull(Msg + ': Interfaces list object should exist', aPackage.Interfaces);
+  AssertEquals(Msg + ': Interfaces count', aExpectedInterfaceCount, aPackage.Interfaces.Count);
+end;
+
+function TTestWITParser.ParseWorld(const aWorldName: String; aExportCount, aImportCount, aUseCount, aTypeDefCount,
+  aIncludeCount: Integer): TWITWorld;
+begin
+  FDocument := FParser.ParseDocument;
+  AssertNotNull('Have Document', FDocument);
+  // Assert Package Details
+  AssertNotNull('Have Package.', FDocument.DefaultPackage);
+  AssertEquals('Have interface', 1, FDocument.DefaultPackage.Worlds.Count);
+  Result := FDocument.DefaultPackage.Worlds[0];
+  AssertWorld('World def', Result, aWorldName, aExportCount, aImportCount, aUseCount, aTypeDefCount, aIncludeCount);
+
+end;
+
+function TTestWITParser.ParseFunc(const aFuncName: String; aArgNames: array of string; aArgTypes: array of TWitTypeKind;
+  aResultType: TWitTypeKind): TWITFunction;
+var
+  LInterface: TWITInterface;
+  i : Integer;
+  lParam : TWITFuncParam;
+begin
+  LInterface:=ParseInterface('funcs',1,0,0);
+  AssertEquals('Have function',TWITFunction,LInterface.Functions[0].ClassType);
+  Result:=LInterface.Functions[0];
+  AssertEquals('function name',aFuncName,Result.Name);
+  AssertNotNull('Function typedef',Result.TypeDef);
+  AssertNotNull('Function params',Result.TypeDef.Parameters);
+  AssertEquals('Args count',Length(aArgNames),Result.TypeDef.Parameters.Count);
+  for I:=0 to Length(aArgNames)-1 do
+    begin
+    lParam:=Result.TypeDef.Parameters[i];
+    AssertNotNull('Function param '+IntTostr(i),lParam);
+    AssertEquals('Function param name'+IntTostr(i),aArgNames[i],lParam.Name);
+    AssertNotNull('Have Function param type '+IntTostr(i),lParam.ParamType);
+    AssertEquals('Function param type kind '+IntTostr(i),aArgTypes[i],lParam.ParamType.Kind);
+    end;
+  if aResultType=wtVoid then
+    AssertNull('No result type',Result.TypeDef.ResultType)
+  else
+    begin
+    AssertNotNull('have result type',Result.TypeDef.ResultType);
+    AssertEquals('result type kind',aResultType,Result.TypeDef.ResultType.Kind)
+    end;
+end;
+
+class function TTestWITParser.AssertResultType(const aMsg: string; aType: TWITTypeDef;const aName: String; aOKKind: TWITTypeKind;
+  aErrorKind: TWitTypeKind; aIgnore: TResultIgnores): TWITResultType;
+
+var
+  lRes : TWITResultType;
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITResultType,aType.TypeDef.ClassType);
+  lRes:=aType.TypeDef as TWITResultType;
+  if riResult in aIgnore then
+    AssertNull(aMsg+': no OK type',lRes.OkType)
+  else
+    begin
+    AssertNotNull(aMsg+': OK type',lRes.OkType);
+    AssertEquals(aMsg+': OK type kind',aOKKind,lRes.OkType.Kind);
+    end;
+  if riError in aIgnore then
+    AssertNull(aMsg+': no Error type',lRes.ErrorType)
+  else
+    begin
+    AssertNotNull(aMsg+': Error type',lRes.ErrorType);
+    AssertEquals(aMsg+': Error type kind',aErrorKind,lRes.ErrorType.Kind);
+    end;
+  Result:=lRes;
+end;
+
+class function TTestWITParser.AssertOptionType(const aMsg: string; aType: TWITTypeDef;const aName: String; aOptionKind: TWITTypeKind
+  ): TWITOptionType;
+var
+  lOpt : TWITOptionType;
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITOptionType,aType.TypeDef.ClassType);
+  lopt:=aType.TypeDef as TWITOptionType;
+  AssertNotNull(aMsg+': item type',lOpt.ItemType);
+  AssertEquals(aMsg+': type kind',aOptionKind,lOpt.ItemType.Kind);
+  Result:=lOpt;
+end;
+
+class function TTestWITParser.AssertStreamType(const aMsg: string; aType: TWITTypeDef;const aName: String; aStreamKind: TWITTypeKind
+  ): TWITStreamType;
+var
+  lStream : TWITStreamType;
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITStreamType,aType.TypeDef.ClassType);
+  lStream:=aType.TypeDef as TWITStreamType;
+  AssertNotNull(aMsg+': item type',lStream.ItemType);
+  AssertEquals(aMsg+': type kind',aStreamKind,lStream.ItemType.Kind);
+  Result:=lStream;
+end;
+
+class function TTestWITParser.AssertFutureType(const aMsg: string; aType: TWITTypeDef;const aName: String; aFutureKind: TWITTypeKind
+  ): TWITFutureType;
+var
+  lFuture : TWITFutureType;
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITFutureType,aType.TypeDef.ClassType);
+  lFuture:=aType.TypeDef as TWitFutureType;
+  AssertNotNull(aMsg+': item type',lFuture.ItemType);
+  AssertEquals(aMsg+': type kind',aFutureKind,lFuture.ItemType.Kind);
+  Result:=lFuture;
+end;
+
+class function TTestWITParser.AssertTupleType(const aMsg: string; aType: TWITTypeDef;const aName: String;
+  aOptionKind: array of TWITTypeKind): TWITTupleType;
+var
+  lTuple : TWITTupleType;
+  I : Integer;
+  S : String;
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITTupleType,aType.TypeDef.ClassType);
+  lTuple:=aType.TypeDef as TWITTupleType;
+  AssertEquals(aMsg+': have correct count',Length(aOptionKind),lTuple.Items.Count);
+  For I:=0 to Length(aOptionKind)-1 do
+    begin
+    S:=Format(': item[%d]',[i]);
+    AssertNotNull(aMsg+S+' type ',lTuple.Items[i]);
+    AssertEquals(aMsg+S+' kind',aOptionKind[i],lTuple.Items[i].Kind);
+    end;
+  Result:=lTuple;
+end;
+
+class function TTestWITParser.AssertFlagsType(const aMsg: string; aType: TWITTypeDef;const aName: String; aFlagNames: array of String
+  ): TWITFlagsType;
+var
+  lFlags : TWITFlagsType;
+  I : Integer;
+  S : String;
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITFlagsType,aType.TypeDef.ClassType);
+  lFlags:=aType.TypeDef as TWITFlagsType;
+  AssertEquals(aMsg+': have correct count',Length(aFlagNames),lFlags.Flags.Count);
+  For I:=0 to Length(aFlagNames)-1 do
+    begin
+    S:=Format(': item[%d]',[i]);
+    AssertEquals(aMsg+S+' name',aFlagNames[i],lFlags.Flags[i]);
+    end;
+  Result:=lFlags;
+end;
+
+class function TTestWITParser.AssertVariantType(const aMsg: string; aType: TWITTypeDef;const aName: String; aVariantNames: array of String
+  ): TWITVariantType;
+var
+  lVariant : TWITVariantType;
+  I : Integer;
+  S : String;
+
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITVariantType,aType.TypeDef.ClassType);
+  lVariant:=aType.TypeDef as TWITVariantType;
+  AssertEquals(aMsg+': have correct count',Length(aVariantNames),lVariant.Cases.Count);
+  For I:=0 to Length(aVariantNames)-1 do
+    begin
+    S:=Format(': item[%d]',[i]);
+    AssertEquals(aMsg+S+' name',aVariantNames[i],lVariant.Cases[i].Name);
+    end;
+  Result:=lVariant;
+end;
+
+class function TTestWITParser.AssertRecordType(const aMsg: string; aType: TWITTypeDef;const aName: String; aFieldNames: array of String;
+  aFieldTypes: array of TWITTypeKind): TWITRecordType;
+var
+  lRecord: TWITRecordType;
+  I : Integer;
+  S : String;
+
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITRecordType,aType.TypeDef.ClassType);
+  lRecord:=aType.TypeDef as TWITRecordType;
+  AssertEquals(aMsg+': have correct count',Length(aFieldNames),lRecord.Fields.Count);
+  For I:=0 to Length(aFieldNames)-1 do
+    begin
+    S:=Format(': field[%d]',[i]);
+    AssertEquals(aMsg+S+' name',aFieldNames[i],lRecord.Fields[i].Name);
+    AssertNotNull(aMsg+S+' type',lRecord.Fields[i].FieldType);
+    AssertEquals(aMsg+S+' kind',aFieldTypes[i],lRecord.Fields[i].FieldType.Kind);
+    end;
+  Result:=lRecord;
+end;
+
+class function TTestWITParser.AssertResourceType(const aMsg: string; aType: TWITTypeDef;const aName: String; aConstructor: boolean;
+  aFunctionNames: array of String): TWITResourceType;
+var
+  lResource: TWITResourceType;
+  I : Integer;
+  S : String;
+  lHaveConstructor : Boolean;
+begin
+  lHaveConstructor:=False;
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITResourceType,aType.TypeDef.ClassType);
+  lResource:=aType.TypeDef as TWITResourceType;
+  AssertEquals(aMsg+': have correct count',Length(aFunctionNames),lResource.Functions.Count);
+  For I:=0 to Length(aFunctionNames)-1 do
+    begin
+    S:=Format(': function[%d]',[i]);
+    AssertEquals(aMsg+S+' name',aFunctionNames[i],lResource.Functions[i].Name);
+    if not lHaveConstructor then
+      begin
+      lHaveConstructor:=ffConstructor in lResource.Functions[i].TypeDef.Flags;
+      if lHaveConstructor then
+        AssertEquals(aMsg+': have name',aName,lResource.Functions[i].Name);
+      end;
+    end;
+  Result:=lResource;
+end;
+
+class function TTestWITParser.AssertAliasType(const aMsg: string; aType: TWITTypeDef;const aName: String; aAliasName: String
+  ): TWITIdentifierType;
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITIdentifierType,aType.TypeDef.ClassType);
+  Result:=aType.TypeDef as TWITIdentifierType;
+  AssertEquals('Alias name ',aAliasName,Result.Name);
+end;
+
+class function TTestWITParser.AssertHandleType(const aMsg: string; aType: TWITTypeDef; const aName: String; aAliasName: String
+  ): TWITHandleType;
+begin
+  AssertNotNull(aMsg+': have type',aType);
+  AssertEquals(aMsg+': have name',aName,aType.Name);
+  AssertNotNull(aMsg+': have typedef',aType.TypeDef);
+  AssertEquals(aMsg+': have typedef',TWITHandleType,aType.TypeDef.ClassType);
+  Result:=aType.TypeDef as TWITHandleType;
+  AssertEquals('Alias name ',aAliasName,Result.Name);
+end;
+
+class function TTestWITParser.AssertTypeDef(const Msg: String; aType: TWITType; aExpectedName: string;
+  aExpectedUnderlyingKind: TWITTypeKind): TWITType;
+var
+  lTypeDef : TWITTypeDef absolute aType;
+begin
+  AssertNotNull(Msg + ': Have Type', aType);
+  AssertEquals(Msg + ': Type is TypeDef', TWITTypeDef, aType.ClassType);
+  AssertNotNull(Msg + ': Have Type.TypeDef', lTypeDef.Typedef);
+  AssertEquals(Msg + ': Type alias name', aExpectedName, lTypeDef.Name);
+  AssertEquals(Msg + ': Underlying type kind of alias', aExpectedUnderlyingKind, lTypeDef.Kind);
+  Result:=lTypeDef.TypeDef;
+end;
+
+
+class function TTestWITParser.AssertListType(const aMsg: string; aTypeDef: TWitTypeDef;const aName: String; aElementKind: TWITTypeKind;
+  aCount: Integer): TWitType;
+var
+  lListDef : TWITListType;
+begin
+  AssertEquals(aMsg+'type name',aName,aTypeDef.Name);
+  AssertEquals(aMsg+'List type',wtList,aTypeDef.Kind);
+  AssertEquals(aMsg+'Typedef class',TWITListType,aTypeDef.TypeDef.ClassType);
+  lListDef:=aTypeDef.TypeDef as TWITListType;
+  AssertEquals(aMsg+'List element type',aElementKind,lListDef.ItemType.Kind);
+  AssertEquals(aMsg+'List element count',aCount,lListDef.ItemCount);
+  Result:=lListDef.ItemType;
+end;
+
+class function TTestWITParser.AssertEnumType(const aMsg: String; aType: TWITTypeDef;const aName: String; aValues: array of string
+  ): TWITEnumType;
+
+var
+  lEnum : TWITEnumType;
+  I : integer;
+
+begin
+  AssertTypeDef(aMsg,aType,aName,wtEnum);
+  AssertEquals(aMsg+': name',aName,aType.Name);
+  AssertEquals(aMsg+': type',TWITEnumType,aType.TypeDef.ClassType);
+  lEnum:=aType.TypeDef as TWITEnumType;
+  AssertEquals(aMsg+': case count',Length(aValues),lEnum.Cases.Count);
+  For I:=0 to Length(aValues)-1 do
+    AssertEquals(aMsg+': case '+IntToStr(i),aValues[i],lEnum.Cases[i]);
+  Result:=lEnum;
+end;
+
+
+{ ---------------------------------------------------------------------
+  Parsing aids
+  ---------------------------------------------------------------------}
+
+function TTestWITParser.ParseInterface(const aName : String; aFunctionCount, aTypeCount, aAnnotationCount : integer) : TWITInterface;
+
+begin
+  FDocument := FParser.ParseDocument;
+  AssertNotNull('Have Document', FDocument);
+  // Assert Package Details
+  AssertNotNull('Have Package.', FDocument.DefaultPackage);
+  AssertEquals('Have interface', 1, FDocument.DefaultPackage.Interfaces.Count);
+  Result := FDocument.DefaultPackage.Interfaces[0];
+  AssertInterface('Interface def', Result, aName, aFunctionCount,aTYpeCount, aAnnotationCount);
+end;
+
+function TTestWITParser.ParseType(const aInterFaceName,aTypeName: String): TWITTypeDef;
+var
+  LInterface: TWITInterface;
+begin
+  LInterface:=ParseInterface(aInterfaceName,0,1,0);
+  AssertEquals('Have Type',TWITTypeDef,LInterface.Types[0].ClassType);
+  Result:=LInterface.Types[0] as TWITTypeDef;
+  AssertEquals('type name',aTypeName,Result.Name);
+end;
+
+{ ---------------------------------------------------------------------
+  Actual tests
+  ---------------------------------------------------------------------}
+
+
+
+procedure TTestWITParser.TestParseExitInterfaceDocument;
+const
+  WIT_CONTENT =
+    '@since(version = 0.2.0)' + sLineBreak +
+    'interface exit {' + sLineBreak +
+    '  @since(version = 0.2.0)' + sLineBreak +
+    '  exit: func(status: result);' + sLineBreak +
+    sLineBreak +
+    '  @unstable(feature = cli-exit-with-code)' + sLineBreak +
+    '  exit-with-code: func(status-code: u8);' + sLineBreak +
+    '}';
+
+var
+  LInterface: TWITInterface;
+  LFunc: TWITFunction;
+  LParam: TWITFuncParam;
+  LParamType: TWITType;
+begin
+  InitParser(WIT_CONTENT);
+  AssertNotNull('Parser should be created.', FParser);
+  FDocument := FParser.ParseDocument;
+  AssertNotNull('ParseDocument should return a valid TWITDocument.', FDocument);
+
+  // Document should contain one interface
+  AssertEquals('Document should contain one interface.', 1, FDocument.Interfaces.Count);
+  lInterface:=FDocument.Interfaces[0];
+  AssertInterface('Exit interface',lInterface,'exit',2,0,1);
+  AssertAnnotation('Intf annotation',LInterface.Annotations[0],'since',['version','0.2.0']);
+
+  // --- Test Function 0: "exit" ---
+  lFunc:=LInterface.Functions[0];
+  AssertFunction('First exit func',lFunc,'exit',1,False,1);
+  AssertAnnotation('First exit func annotation',lFunc.Annotations[0],'since',['version','0.2.0']);
+
+  LParam := LFunc.TypeDef.Parameters[0];
+  AssertFunctionParam('Parameter 0 of function "exit"',LParam,'status',wtResult,'');
+  lParamType:=lParam.ParamType;
+  AssertEquals('Parameter status type',TWITResultType,LParamType.ClassType);
+  AssertNull('OkType for shorthand "result" should be nil or an empty type representation.', (LParamType as TWITResultType).OkType);
+  AssertNull('ErrorType for shorthand "result" should be nil or an empty type representation.', (LParamType as TWITResultType).ErrorType);
+
+  // --- Test Function 1: "exit-with-code" ---
+  LFunc := LInterface.Functions[1];
+  AssertFunction('Second exit func',lFunc,'exit-with-code',1,False,1);
+  AssertAnnotation('Second exit func annotation',lFunc.Annotations[0],'unstable',['feature','cli-exit-with-code']);
+
+  LParam := LFunc.TypeDef.Parameters[0];
+  AssertFunctionParam('Parameter 0 of function "exit-with-code"',LParam,'status-code',wtU8,'');
+end;
+
+procedure TTestWITParser.TestSimpleTypes;
+
+const
+  WIT_CONTENT =
+    'package foo:types;' + sLineBreak +
+    sLineBreak +
+    'interface types {' + sLineBreak +
+    '  type t1 = u8;' + sLineBreak +
+    '  type t2 = u16;' + sLineBreak +
+    '  type t3 = u32;' + sLineBreak +
+    '  type t4 = u64;' + sLineBreak +
+    '  type t5 = s8;' + sLineBreak +
+    '  type t6 = s16;' + sLineBreak +
+    '  type t7 = s32;' + sLineBreak +
+    '  type t8 = s64;' + sLineBreak +
+    '  type t9a = f32;' + sLineBreak +
+    '  type t9b = f32;' + sLineBreak + // Duplicate type kind, different name
+    '  type t10a = f64;' + sLineBreak +
+    '  type t10b = f64;' + sLineBreak + // Duplicate type kind, different name
+    '  type t11 = char;' + sLineBreak + // Assuming char maps to wtU32
+    '  type t12 = string;' + sLineBreak +
+    '}';
+var
+  LInterface: TWITInterface;
+begin
+  InitParser(WIT_CONTENT);
+  LInterface := ParseInterface('types',0,14,0);
+  AssertTypeDef('Type t1 = u8', LInterface.Types[0], 't1', wtU8);
+  AssertTypeDef('Type t2 = u16', LInterface.Types[1], 't2', wtU16);
+  AssertTypeDef('Type t3 = u32', LInterface.Types[2], 't3', wtU32);
+  AssertTypeDef('Type t4 = u64', LInterface.Types[3], 't4', wtU64);
+  AssertTypeDef('Type t5 = s8', LInterface.Types[4], 't5', wtS8);
+  AssertTypeDef('Type t6 = s16', LInterface.Types[5], 't6', wtS16);
+  AssertTypeDef('Type t7 = s32', LInterface.Types[6], 't7', wtS32);
+  AssertTypeDef('Type t8 = s64', LInterface.Types[7], 't8', wtS64);
+  AssertTypeDef('Type t9a = f32', LInterface.Types[8], 't9a', wtFloat32);
+  AssertTypeDef('Type t9b = f32', LInterface.Types[9], 't9b', wtFloat32);
+  AssertTypeDef('Type t10a = f64', LInterface.Types[10], 't10a', wtFloat64);
+  AssertTypeDef('Type t10b = f64', LInterface.Types[11], 't10b', wtFloat64);
+  AssertTypeDef('Type t11 = char', LInterface.Types[12], 't11', wtChar);
+  AssertTypeDef('Type t12 = string', LInterface.Types[13], 't12', wtString);
+end;
+
+procedure TTestWITParser.TestListType;
+
+const
+  WIT_CONTENT = 'list<char>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT, true));
+  lTypeDef:=ParseType('types','a');
+  AssertListType('List',lTypeDef,'a',wtChar);
+end;
+
+
+procedure TTestWITParser.TestListListType;
+const
+  WIT_CONTENT = 'list<list<list<t32>>>';
+var
+  lTypeDef : TWITTypeDef;
+  lItem : TWITType;
+  lList : TWITListType absolute litem;
+  lIdent : TWITIdentifierType;
+
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT, true));
+  lTypeDef:=ParseType('types','a');
+  lItem:=AssertListType('List',lTypeDef,'a',wtList);
+  AssertEquals('Item is list class',TWITListType,lItem.ClassType);
+  AssertEquals('Item.Item is list class',TWITListType,lList.ItemType.ClassType);
+  lItem:=lList.ItemType;
+  AssertEquals('Item.Item.Item is identifier class',TWITIdentifierType,lList.ItemType.ClassType);
+  lIdent:=lList.ItemType as TWITIdentifierType;
+  AssertEquals('Item.Item.Item  name','t32',lIdent.Name);
+end;
+
+procedure TTestWITParser.TestListSized;
+
+const
+  WIT_CONTENT = 'list<u32, 4>';
+
+var
+  lTypeDef : TWITTypeDef;
+
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  lTypeDef:=ParseType('types','a');
+  AssertListType('List',lTypeDef,'a',wtU32,4);
+end;
+
+procedure TTestWITParser.TestListSizedListSized;
+
+const
+  WIT_CONTENT = 'list<list<u32, 4>, 2>';
+
+var
+  lTypeDef : TWITTypeDef;
+  lItem : TWITType;
+  lList : TWITListType absolute lItem;
+
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT, true));
+  lTypeDef:=ParseType('types','a');
+  lItem:=AssertListType('List',lTypeDef,'a',wtList,2);
+  AssertEquals('Item class',TWITListType,lItem.ClassType);
+  AssertEquals('List list item',wtu32,lList.ItemType.Kind);
+  AssertEquals('List list item count',4,lList.ItemCount);
+end;
+
+
+procedure TTestWITParser.TestEnum;
+const
+  WIT_CONTENT = 'enum a {one,two,three}';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  lTypeDef:=ParseType('types','a');
+  AssertEnumType('Enum type',lTypeDef,'a',['one','two','three']);
+end;
+
+procedure TTestWITParser.TestEnumEndWithComma;
+const
+  WIT_CONTENT = 'enum a {one,two,three,}';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  lTypeDef:=ParseType('types','a');
+  AssertEnumType('Enum type',lTypeDef,'a',['one','two','three']);
+end;
+
+
+
+procedure TTestWITParser.TestResultEmpty;
+const
+  WIT_CONTENT = 'result';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  lTypeDef:=ParseType('types','a');
+  AssertResultType('Result type',LTypeDef,'a',wtu32,wtu8,[riResult,riError]);
+end;
+
+procedure TTestWITParser.TestResultOneType;
+const
+  WIT_CONTENT = 'result<u32>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  lTypeDef:=ParseType('types','a');
+  AssertResultType('Result type',LTypeDef,'a',wtu32,wtu8,[riError]);
+end;
+
+procedure TTestWITParser.TestResultTwoTypes;
+const
+  WIT_CONTENT = 'result<u32,u8>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  lTypeDef:=ParseType('types','a');
+  AssertResultType('Result type',LTypeDef,'a',wtu32,wtu8,[]);
+end;
+
+procedure TTestWITParser.TestResultOneIgnoredTyoe;
+const
+  WIT_CONTENT = 'result<_,u32>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  lTypeDef:=ParseType('types','a');
+  AssertResultType('Result type',LTypeDef,'a',wtu32,wtu32,[riResult]);
+end;
+
+procedure TTestWITParser.TestOption;
+const
+  WIT_CONTENT = 'option<u32>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertOptionType('Option type',LTypeDef,'a',wtu32);
+end;
+
+procedure TTestWITParser.TestStream;
+const
+  WIT_CONTENT = 'stream<u32>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertStreamType('Stream type',LTypeDef,'a',wtu32);
+end;
+
+procedure TTestWITParser.TestStreamEmpty;
+const
+  WIT_CONTENT = 'stream';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertStreamType('Stream type',LTypeDef,'a',wtVoid);
+end;
+
+procedure TTestWITParser.TestFuture;
+const
+  WIT_CONTENT = 'future<u32>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertFutureType('Future type',LTypeDef,'a',wtu32);
+end;
+
+
+procedure TTestWITParser.TestNestedFuture;
+const
+  WIT_CONTENT = 'option<stream<future>>';
+var
+  lTypeDef : TWITTypeDef;
+  lOpt : TWITOptionType;
+  lStream : TWITStreamType;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  lOpt:=AssertOptionType('Option type',LTypeDef,'a',wtStream);
+  AssertEquals('Stream type',TWITStreamType,lOpt.ItemType.ClassType);
+  lStream:=lOpt.ItemType as TWITStreamType;
+  AssertEquals('Future type',TWITFutureType,lStream.ItemType.Classtype);
+end;
+
+procedure TTestWITParser.TestFutureEmpty;
+const
+  WIT_CONTENT = 'future';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertFutureType('Future type',LTypeDef,'a',wtVoid);
+end;
+
+procedure TTestWITParser.TestTupleEmpty;
+const
+  WIT_CONTENT = 'tuple<>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertTupleType('Tuple type',LTypeDef,'a',[]);
+end;
+
+procedure TTestWITParser.TestTuple1;
+const
+  WIT_CONTENT = 'tuple<u32>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertTupleType('Tuple type',LTypeDef,'a',[wtu32]);
+end;
+
+procedure TTestWITParser.TestTuple2;
+const
+  WIT_CONTENT = 'tuple<u32, u64>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertTupleType('Tuple type',LTypeDef,'a',[wtu32,wtu64]);
+end;
+
+procedure TTestWITParser.TestTuple3;
+const
+  WIT_CONTENT = 'tuple<u32, u64, u8>';
+var
+  lTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertTupleType('Tuple type',LTypeDef,'a',[wtu32,wtu64,wtu8]);
+end;
+
+procedure TTestWITParser.TestTupleComma;
+const
+  WIT_CONTENT = 'tuple<u32,>';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertTupleType('Tuple type',LTypeDef,'a',[wtU32]);
+end;
+
+procedure TTestWITParser.TestFlagsEmpty;
+const
+  WIT_CONTENT = 'flags a {}';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertFlagsType('Tuple type',LTypeDef,'a',[]);
+end;
+
+procedure TTestWITParser.TestFlags1;
+const
+  WIT_CONTENT = 'flags a {a}';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertFlagsType('Tuple type',LTypeDef,'a',['a']);
+end;
+
+procedure TTestWITParser.TestFlags2;
+const
+  WIT_CONTENT = 'flags a {a, b}';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertFlagsType('Tuple type',LTypeDef,'a',['a','b']);
+end;
+
+procedure TTestWITParser.TestFlags3;
+const
+  WIT_CONTENT = 'flags a {a, b, c}';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertFlagsType('Tuple type',LTypeDef,'a',['a','b','c']);
+end;
+
+procedure TTestWITParser.TestFlagsComma;
+const
+  WIT_CONTENT = 'flags a {a, b, c, }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertFlagsType('Tuple type',LTypeDef,'a',['a','b','c']);
+end;
+
+procedure TTestWITParser.TestVariant1;
+const
+  WIT_CONTENT = 'variant a { a }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertVariantType('Variant type',LTypeDef,'a',['a']);
+end;
+
+procedure TTestWITParser.TestVariant2;
+const
+  WIT_CONTENT = 'variant a { a, b }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertVariantType('Variant type',LTypeDef,'a',['a','b']);
+end;
+
+procedure TTestWITParser.TestVariant2Comma;
+const
+  WIT_CONTENT = 'variant a { a, b,  }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertVariantType('Variant type',LTypeDef,'a',['a','b']);
+end;
+
+(*
+variant t36 { a, b(u32), }
+variant t37 { a, b(option<u32>), }
+*)
+
+procedure TTestWITParser.TestVariantTypedSimple;
+const
+  WIT_CONTENT = 'variant a { a, b(u32) }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertVariantType('Variant type',LTypeDef,'a',['a','b']);
+end;
+
+procedure TTestWITParser.TestVariantTypedSimpleComma;
+const
+  WIT_CONTENT = 'variant a { a, b(u32), }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertVariantType('Variant type',LTypeDef,'a',['a','b']);
+end;
+
+procedure TTestWITParser.TestVariantTypedComplex;
+const
+  WIT_CONTENT = 'variant a { a, b(option<u32>) }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertVariantType('Variant type',LTypeDef,'a',['a','b']);
+end;
+
+procedure TTestWITParser.TestRecordEmpty;
+const
+  WIT_CONTENT = 'record a {}';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertRecordType('Record type',LTypeDef,'a',[],[]);
+end;
+
+procedure TTestWITParser.TestRecord1;
+const
+  WIT_CONTENT = 'record a { a: u32 }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertRecordType('Record type',LTypeDef,'a',['a'],[wtU32]);
+end;
+
+procedure TTestWITParser.TestRecord2;
+const
+  WIT_CONTENT = 'record a { a: u32, b: u64  }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertRecordType('Record type',LTypeDef,'a',['a','b'],[wtU32,wtU64]);
+end;
+
+procedure TTestWITParser.TestRecord2Comma;
+const
+  WIT_CONTENT = 'record a { a: u32, b: u64,  }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertRecordType('Record type',LTypeDef,'a',['a','b'],[wtU32,wtU64]);
+end;
+
+procedure TTestWITParser.TestRecordRecordName;
+const
+  WIT_CONTENT = 'record %record { a: u32, b: u64,  }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','record');
+  AssertRecordType('Record type',LTypeDef,'record',['a','b'],[wtU32,wtU64]);
+end;
+
+procedure TTestWITParser.TestAlias;
+const
+  WIT_CONTENT = 'b';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  AssertAliasType('Alias type',LTypeDef,'a','b');
+end;
+
+procedure TTestWITParser.TestBorrowedHandle;
+const
+  WIT_CONTENT = 'borrow<b>';
+var
+  LTypeDef : TWITTypeDef;
+  lIdent : TWITHandleType;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT,True));
+  LTypeDef:=ParseType('types','a');
+  lIdent:=AssertHandleType(' type',LTypeDef,'a','b');
+  AssertTrue('Borrowed',lIdent.Borrowed);
+end;
+
+procedure TTestWITParser.TestResourceEmpty;
+const
+  WIT_CONTENT = 'resource a;';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertResourceType('Resource type',LTypeDef,'a',false, []);
+end;
+
+procedure TTestWITParser.TestResourceEmpty2;
+const
+  WIT_CONTENT = 'resource a {}';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertResourceType('Resource type',LTypeDef,'a',false, []);
+end;
+
+procedure TTestWITParser.TestResourceConstructor;
+const
+  WIT_CONTENT = 'resource a { constructor (c:u8); }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertResourceType('Resource type',LTypeDef,'a',true, ['a']);
+end;
+
+procedure TTestWITParser.TestResourceOneMethod;
+const
+  WIT_CONTENT = 'resource a { write : func (c:u8); }';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertResourceType('Resource type',LTypeDef,'a',true, ['write']);
+end;
+
+procedure TTestWITParser.TestResourceStaticMethod;
+const
+  WIT_CONTENT = 'resource a { write : static func (c:u8); }';
+var
+  LTypeDef : TWITTypeDef;
+  lRes : TWITResourceType;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  lRes:=AssertResourceType('Resource type',LTypeDef,'a',true, ['write']);
+  AssertTrue('Function marked static',(ffStatic in lRes.Functions[0].TypeDef.Flags));
+end;
+
+procedure TTestWITParser.TestResourceAsyncMethod;
+const
+  WIT_CONTENT = 'resource a { write : async func (c:u8); }';
+var
+  LTypeDef : TWITTypeDef;
+  lRes : TWITResourceType;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  lRes:=AssertResourceType('Resource type',LTypeDef,'a',true, ['write']);
+  AssertTrue('Function marked static',(ffAsync in lRes.Functions[0].TypeDef.Flags));
+end;
+
+procedure TTestWITParser.TestResourceTwoMethods;
+const
+  WIT_CONTENT = 'resource a { '+sLineBreak+
+                ' read : func (c:u8) -> list<u8>; '+sLineBreak+
+                ' write : func (c : list<u8>); '+sLineBreak+
+                '}';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertResourceType('Resource type',LTypeDef,'a',false, ['read','write']);
+end;
+
+procedure TTestWITParser.TestResourceOneMethodAndConstructor;
+const
+  WIT_CONTENT = 'resource a { '+sLineBreak+
+                ' read : func (c:u8) -> list<u8>; '+sLineBreak+
+                ' constructor (c : list<u8>); '+sLineBreak+
+                '}';
+var
+  LTypeDef : TWITTypeDef;
+begin
+  InitParser(WrapTypeDef(WIT_CONTENT));
+  LTypeDef:=ParseType('types','a');
+  AssertResourceType('Resource type',LTypeDef,'a',true, ['read','a']);
+end;
+
+procedure TTestWITParser.TestUseIdentifier;
+const
+  WIT_CONTENT = 'use a;';
+var
+  lUse : TWITTopLevelUse;
+begin
+  InitParser(WIT_CONTENT);
+  FDocument := FParser.ParseDocument;
+  AssertEquals('Uses count', 1, FDocument.UseStatements.Count);
+  lUse:=FDocument.UseStatements[0];
+  AssertUse('Simple use',lUse,'','','a','',[]);
+end;
+
+procedure TTestWITParser.TestUseIdentifierAs;
+const
+  WIT_CONTENT = 'use a as b;';
+var
+  lUse : TWITTopLevelUse;
+begin
+  InitParser(WIT_CONTENT);
+  FDocument := FParser.ParseDocument;
+  AssertEquals('Uses count', 1, FDocument.UseStatements.Count);
+  lUse:=FDocument.UseStatements[0];
+  AssertUse('Simple use',lUse,'','','a','b',[]);
+end;
+
+procedure TTestWITParser.TestUseFullIdentifier;
+const
+  WIT_CONTENT = 'use d:c/a;';
+var
+  lUse : TWITTopLevelUse;
+begin
+  InitParser(WIT_CONTENT);
+  FDocument := FParser.ParseDocument;
+  AssertEquals('Uses count', 1, FDocument.UseStatements.Count);
+  lUse:=FDocument.UseStatements[0];
+  AssertUse('Full use',lUse,'c','','a','',['d']);
+end;
+
+procedure TTestWITParser.TestUseFullIdentifierVersion;
+const
+  WIT_CONTENT = 'use d:c/[email protected];';
+var
+  lUse : TWITTopLevelUse;
+begin
+  InitParser(WIT_CONTENT);
+  FDocument := FParser.ParseDocument;
+  AssertEquals('Uses count', 1, FDocument.UseStatements.Count);
+  lUse:=FDocument.UseStatements[0];
+  AssertUse('Full use',lUse,'c','1.1.1','a','',['d']);
+end;
+
+procedure TTestWITParser.TestUseFullIdentifierAs;
+const
+  WIT_CONTENT = 'use d:c/a as b;';
+var
+  lUse : TWITTopLevelUse;
+begin
+  InitParser(WIT_CONTENT);
+  FDocument := FParser.ParseDocument;
+  AssertEquals('Uses count', 1, FDocument.UseStatements.Count);
+  lUse:=FDocument.UseStatements[0];
+  AssertUse('Full use',lUse,'c','','a','b',['d']);
+end;
+
+procedure TTestWITParser.TestUseFullIdentifierVersionAs;
+const
+  WIT_CONTENT = 'use d:c/[email protected] as b;';
+var
+  lUse : TWITTopLevelUse;
+begin
+  InitParser(WIT_CONTENT);
+  FDocument := FParser.ParseDocument;
+  AssertEquals('Uses count', 1, FDocument.UseStatements.Count);
+  lUse:=FDocument.UseStatements[0];
+  AssertUse('Full use',lUse,'c','1.1.1','a','b',['d']);
+end;
+
+procedure TTestWITParser.TestParseFunctionEmpty;
+const
+  WIT_CONTENT = '';
+
+begin
+  InitParser(WrapFunc(WIT_CONTENT));
+  ParseFunc('a',[],[],wtVoid);
+end;
+
+procedure TTestWITParser.TestParseFunctionEmptyResult;
+const
+  WIT_CONTENT = '';
+
+begin
+  InitParser(WrapFunc(WIT_CONTENT,'u8'));
+  ParseFunc('a',[],[],wtU8);
+end;
+
+procedure TTestWITParser.TestParseFunctionOneParam;
+const
+  WIT_CONTENT = 'b:u8';
+
+begin
+  InitParser(WrapFunc(WIT_CONTENT));
+  ParseFunc('a',['b'],[wtU8],wtVoid);
+end;
+
+procedure TTestWITParser.TestParseFunctionOneParamResult;
+const
+  WIT_CONTENT = 'b:u8';
+
+begin
+  InitParser(WrapFunc(WIT_CONTENT,'u32'));
+  ParseFunc('a',['b'],[wtU8],wtU32);
+end;
+
+procedure TTestWITParser.TestParseFunctionTwoParams;
+const
+  WIT_CONTENT = 'b : u8, c : list<u8>';
+
+begin
+  InitParser(WrapFunc(WIT_CONTENT));
+  ParseFunc('a',['b','c'],[wtU8, wtList],wtVoid);
+end;
+
+procedure TTestWITParser.TestParseFunctionTwoParamsResult;
+const
+  WIT_CONTENT = 'b : u8, c : list<u8>';
+
+begin
+  InitParser(WrapFunc(WIT_CONTENT,'result<_,u32>'));
+  ParseFunc('a',['b','c'],[wtU8, wtList],wtResult);
+end;
+
+procedure TTestWITParser.TestParseWorldEmpty;
+
+const
+  WIT_CONTENT = 'world a {}';
+
+begin
+  InitParser(WIT_CONTENT);
+  ParseWorld('a',0,0,0,0,0);
+end;
+
+procedure TTestWITParser.TestParseWorldUse;
+const
+  WIT_CONTENT = 'world a { use b.{c}; }';
+
+var
+  lWorld : TWITWorld;
+  lUse : TWITUse;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,1,0,0);
+  lUse:=lWorld.UsesList[0];
+  AssertEquals('Export name','b',lUse.Path.Identifier);
+end;
+
+procedure TTestWITParser.TestParseWorldUseAnnotation;
+const
+  WIT_CONTENT = 'world a { @since(version = 1.1.1)  use b.{c}; }';
+
+var
+  lWorld : TWITWorld;
+  lUse : TWITUse;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,1,0,0);
+  lUse:=lWorld.UsesList[0];
+  AssertEquals('Export name','b',lUse.Path.Identifier);
+  AssertEquals('Have annotation',1,lUse.Annotations.Count);
+  AssertEquals('since annotation','since',lUse.Annotations[0].Name);
+end;
+
+procedure TTestWITParser.TestParseWorldExport;
+const
+  WIT_CONTENT = 'world a { export b; }';
+
+var
+  lWorld : TWITWorld;
+  lExport : TWITExchange;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',1,0,0,0,0);
+  lExport:=lWorld.Exported[0];
+  AssertEquals('Export name','b',lExport.Name);
+end;
+
+procedure TTestWITParser.TestParseWorldExportUse;
+const
+  WIT_CONTENT = 'world a { export b:c/[email protected]; }';
+
+var
+  lWorld : TWITWorld;
+  lExport : TWITExchange;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',1,0,0,0,0);
+  lExport:=lWorld.Exported[0];
+  AssertEquals('Export name','b:c/[email protected]',lExport.Name);
+end;
+
+procedure TTestWITParser.TestParseWorldExportFunction;
+const
+  WIT_CONTENT = 'world a { export b:func (c:u32) ; }';
+
+var
+  lWorld : TWITWorld;
+  lExport : TWITExchange;
+  lExportFunc : TWITExchangeFunc absolute lExport;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',1,0,0,0,0);
+  lExport:=lWorld.Exported[0];
+  AssertEquals('Export name','b',lExport.Name);
+  AssertEquals('export class',TWITExchangeFunc,lExport.ClassType);
+  AssertNotNull('export typedef',lExportFunc.TypeDef);
+end;
+
+procedure TTestWITParser.TestParseWorldExportInterface;
+const
+  WIT_CONTENT = 'world a { export b:interface { c: func (d:u32) ; } }';
+
+var
+  lWorld : TWITWorld;
+  lExport : TWITExchange;
+  lExportIntf : TWITExchangeInterface absolute lExport;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',1,0,0,0,0);
+  lExport:=lWorld.Exported[0];
+  AssertEquals('Export name','b',lExport.Name);
+  AssertEquals('export class',TWITExchangeInterface,lExport.ClassType);
+  AssertInterface('counts',lExportIntf.TypeDef,'b',1,0,0)
+end;
+
+procedure TTestWITParser.TestParseWorldImport;
+const
+  WIT_CONTENT = 'world a { import b; }';
+
+var
+  lWorld : TWITWorld;
+  lExport : TWITExchange;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,1,0,0,0);
+  lExport:=lWorld.Imported[0];
+  AssertEquals('Import name','b',lExport.Name);
+end;
+
+procedure TTestWITParser.TestParseWorldImportUse;
+const
+  WIT_CONTENT = 'world a { import b:c/[email protected]; }';
+
+var
+  lWorld : TWITWorld;
+  lExport : TWITExchange;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,1,0,0,0);
+  lExport:=lWorld.Imported[0];
+  AssertEquals('Export name','b:c/[email protected]',lExport.Name);
+end;
+
+procedure TTestWITParser.TestParseWorldImportFunction;
+const
+  WIT_CONTENT = 'world a { import b:func (c:u32) ; }';
+
+var
+  lWorld : TWITWorld;
+  lExport : TWITExchange;
+  lExportFunc : TWITExchangeFunc absolute lExport;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,1,0,0,0);
+  lExport:=lWorld.Imported[0];
+  AssertEquals('Export name','b',lExport.Name);
+  AssertEquals('export class',TWITExchangeFunc,lExport.ClassType);
+  AssertNotNull('export typedef',lExportFunc.TypeDef);
+end;
+
+procedure TTestWITParser.TestParseWorldImportInterface;
+const
+  WIT_CONTENT = 'world a { import b:interface { c: func (d:u32) ; } }';
+
+var
+  lWorld : TWITWorld;
+  lExport : TWITExchange;
+  lExportIntf : TWITExchangeInterface absolute lExport;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,1,0,0,0);
+  lExport:=lWorld.Imported[0];
+  AssertEquals('Export name','b',lExport.Name);
+  AssertEquals('export class',TWITExchangeInterface,lExport.ClassType);
+  AssertInterface('counts',lExportIntf.TypeDef,'b',1,0,0)
+end;
+
+procedure TTestWITParser.TestParseWorldInclude;
+const
+  WIT_CONTENT = 'world a { include b; }';
+
+var
+  lWorld : TWITWorld;
+  lInclude : TWITInclude;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,0,0,1);
+  lInclude:=lWorld.Includes[0];
+  AssertInclude('First',lInclude,'b',[],[]);
+end;
+
+procedure TTestWITParser.TestParseWorldIncludeUse;
+const
+  WIT_CONTENT = 'world a { include b:c/d; }';
+
+var
+  lWorld : TWITWorld;
+  lInclude : TWITInclude;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,0,0,1);
+  lInclude:=lWorld.Includes[0];
+  AssertInclude('First',lInclude,'b:c/d',[],[]);
+end;
+
+procedure TTestWITParser.TestParseWorldIncludeUseList;
+const
+  WIT_CONTENT = 'world a { include b:c/d with { e as f } }';
+
+var
+  lWorld : TWITWorld;
+  lInclude : TWITInclude;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,0,0,1);
+  lInclude:=lWorld.Includes[0];
+  AssertInclude('First',lInclude,'b:c/d',['e'],['f']);
+end;
+
+procedure TTestWITParser.TestParseWorldIncludeUseList2;
+const
+  WIT_CONTENT = 'world a { include b:c/d with { e as f, g as h } }';
+
+var
+  lWorld : TWITWorld;
+  lInclude : TWITInclude;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,0,0,1);
+  lInclude:=lWorld.Includes[0];
+  AssertInclude('First',lInclude,'b:c/d',['e','g'],['f','h']);
+end;
+
+procedure TTestWITParser.TestParseWorldTypeDef;
+const
+  WIT_CONTENT = 'world a { type x = u32; }';
+
+var
+  lWorld : TWITWorld;
+  lType : TWITTypeDef;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,0,1,0);
+  lType:=lWorld.TypeDefs[0];
+  AssertTypeDef('type',lType,'x',wtU32);
+end;
+
+procedure TTestWITParser.TestParseWorldEnumType;
+const
+  WIT_CONTENT = 'world a { enum x {y,z} }';
+
+var
+  lWorld : TWITWorld;
+  lType : TWITTypeDef;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,0,1,0);
+  lType:=lWorld.TypeDefs[0];
+  AssertEnumType('type',lType,'x',['y','z']);
+end;
+
+procedure TTestWITParser.TestParseWorldVariantType;
+const
+  WIT_CONTENT = 'world a { variant x { y, z} }';
+
+var
+  lWorld : TWITWorld;
+  lType : TWITTypeDef;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,0,1,0);
+  lType:=lWorld.TypeDefs[0];
+  AssertVariantType('type',lType,'x',['y','z']);
+end;
+
+procedure TTestWITParser.TestParseWorldRecordType;
+const
+  WIT_CONTENT = 'world a { record x { y: u32, z: u8} }';
+
+var
+  lWorld : TWITWorld;
+  lType : TWITTypeDef;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,0,1,0);
+  lType:=lWorld.TypeDefs[0];
+  AssertRecordType('type',lType,'x',['y','z'],[wtu32,wtu8]);
+end;
+
+procedure TTestWITParser.TestParseWorldFlagsType;
+const
+  WIT_CONTENT = 'world a { flags x { y, z} }';
+
+var
+  lWorld : TWITWorld;
+  lType : TWITTypeDef;
+
+begin
+  InitParser(WIT_CONTENT);
+  lWorld:=ParseWorld('a',0,0,0,1,0);
+  lType:=lWorld.TypeDefs[0];
+  AssertFlagsType('type',lType,'x',['y','z']);
+end;
+
+procedure TTestWITParser.TestParseInterfaceUse;
+const
+  WIT_CONTENT = 'interface a { use b.{c}; }';
+
+begin
+  InitParser(WIT_CONTENT);
+  FDocument := FParser.ParseDocument;
+
+  AssertNotNull('Have package', FDocument.DefaultPackage);
+  AssertEquals('Interface count.', 1, FDocument.Interfaces.Count);
+  AssertEquals('Use count.', 1, FDocument.Interfaces[0].UseList.Count);
+end;
+
+procedure TTestWITParser.TestParseInterfaceUseGate;
+const
+  WIT_CONTENT = 'interface a { @since (version = 1.1.1) use b.{c}; }';
+
+begin
+  InitParser(WIT_CONTENT);
+  FDocument := FParser.ParseDocument;
+
+  AssertNotNull('Have package', FDocument.DefaultPackage);
+  AssertEquals('Interface count.', 1, FDocument.Interfaces.Count);
+  AssertEquals('Use count.', 1, FDocument.Interfaces[0].UseList.Count);
+  AssertEquals('Use annotation count.', 1, FDocument.Interfaces[0].UseList[0].Annotations.Count);
+end;
+
+procedure TTestWITParser.TestParsePackageEmpty;
+const
+  WIT_CONTENT = 'package foo:empty;';
+var
+  LPackage: TWITPackage;
+begin
+  InitParser(WIT_CONTENT);
+  FDocument := FParser.ParseDocument;
+
+  AssertNotNull('Have package', FDocument.DefaultPackage);
+  AssertEquals('Interface count.', 0, FDocument.Interfaces.Count);
+  AssertEquals('World count.', 0, FDocument.Worlds.Count);
+
+  LPackage := FDocument.DefaultPackage;
+  AssertPackage('Parsed package "foo:empty"', LPackage,
+    'foo', 'empty', '', 0, 0, 0, 0, 0);
+end;
+
+procedure TTestWITParser.TestParsePackageVersions;
+const
+  ScenarioCount = 9;
+  Scenarios : array [1..ScenarioCount] of string = (
+  'package a:[email protected] {}',
+  'package a:[email protected] {}',
+  'package a:[email protected] {}',
+  'package a:[email protected]+a {}',
+  'package a:[email protected]+1 {}',
+  'package a:[email protected]+1a {}',
+  'package a:[email protected] {}',
+  'package a:[email protected] {}',
+  'package a:[email protected] {}'
+  );
+var
+  I,p : Integer;
+  lScenario,
+  lVersion : string;
+  lMessage : String;
+begin
+  For I:=1 to ScenarioCount do
+    begin
+    lScenario:=Scenarios[i];
+    P:=Pos('@',lScenario);
+    lVersion:=Copy(lScenario,P+1,pos('{',lScenario)-2-P);
+    lMessage:=Format('Scenario[%d] "%s": ',[i,lScenario]);
+    InitParser(lScenario);
+    try
+      FDocument:=FParser.ParseDocument;
+    except
+      on E : Exception do
+        Fail('Exception %s during scenario %s: "%s"',[E.ClassName,lMessage,E.Message]);
+    end;
+    AssertEquals('Have package ',1,FDocument.Packages.Count);
+    AssertPackage(lMessage,FDocument.Packages[0],'a','b',lVersion)
+    end;
+end;
+
+initialization
+  RegisterTest(TTestWITParser);
+end.

+ 466 - 0
packages/fcl-wit/tests/utcwitscanner.pp

@@ -0,0 +1,466 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2025 Michael Van Canneyt ([email protected])
+
+    Test WIT scanner
+
+    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.
+
+ **********************************************************************}
+
+unit utcwitscanner;
+
+interface
+
+uses
+  TestUtils, FPCUnit, Classes, SysUtils, testregistry, streamex, WIT.Scanner;
+
+type
+
+  { TWITScannerTests }
+
+  TWITScannerTests = class(TTestCase)
+  Private
+    FToken: TToken;
+  protected
+    Procedure TestToken(aSource: string; aToken: TTokenType);
+  public
+    Procedure SetUp; override;
+    class procedure AssertEquals(Msg : string; aExpected,aActual : TTokenType); overload;
+  published
+    procedure TestUnknown;
+    procedure TestWhitespace;
+    procedure TestEOF;
+    procedure TestOpenRoundBrace;
+    procedure TestCloseRoundBrace;
+    procedure TestOpenCurlyBrace;
+    procedure TestCloseCurlyBrace;
+    procedure TestOpenSquareBrace;
+    procedure TestCloseSquareBrace;
+    procedure TestColon;
+    procedure TestSemicolon;
+    procedure TestComma;
+    procedure TestEqual;
+    procedure TestDot;
+    procedure TestPlus;
+    procedure TestMinus;
+    procedure TestStar;
+    procedure TestDiv;
+    procedure TestLessThan;
+    procedure TestGreaterThan;
+    procedure TestAt;
+    procedure TestArrow;
+    procedure TestNumber;
+    procedure TestString;
+    procedure TestInterface;
+    procedure TestRecord;
+    procedure TestFlags;
+    procedure TestEnum;
+    procedure TestType;
+    procedure TestResource;
+    procedure TestImport;
+    procedure TestExport;
+    procedure TestUse;
+    procedure TestWorld;
+    procedure TestPackage;
+    procedure TestAs;
+    procedure TestAsync;
+    procedure TestBool;
+    procedure TestBorrow;
+    procedure TestChar;
+    procedure TestConstructor;
+    procedure TestFrom;
+    procedure TestFunc;
+    procedure TestFuture;
+    procedure TestInclude;
+    procedure TestList;
+    procedure TestOption;
+    procedure TestOwn;
+    procedure TestResult;
+    procedure TestStatic;
+    procedure TestStream;
+    procedure TestStringKW;
+    procedure TestTuple;
+    procedure TestVariant;
+    procedure TestWith;
+    procedure TestIdentifier;
+    procedure TestIdentifierEscapedKeyword;
+    procedure TestIdentifierDash;
+    Procedure TestSingleComment;
+    Procedure TestMultiComment;
+    Procedure TestMultiComment2;
+    Procedure TestMultiCommentNested;
+  end;
+
+implementation
+
+uses TypInfo;
+
+{ TWITScannerTests }
+
+procedure TWITScannerTests.TestToken(aSource: string; aToken: TTokenType);
+var
+  Scanner: TWITScanner;
+  Reader: TTextReader;
+  lToken : TToken;
+begin
+  Reader := TStringReader.Create(aSource);
+  try
+    Scanner := TWITScanner.Create(Reader);
+    try
+      Reader:=nil;
+      FToken := Scanner.GetToken;
+      AssertEquals('Token type mismatch', aToken, FToken.TokenType);
+      if FToken.TokenType<>ttEOF then
+        begin
+        LToken := Scanner.GetToken;
+        AssertEquals('EOF reached',ttEOF,lToken.TokenType);
+        end;
+    finally
+      Scanner.Free;
+    end;
+  finally
+    Reader.Free;
+  end;
+end;
+
+procedure TWITScannerTests.SetUp;
+begin
+  inherited SetUp;
+  FToken:=Default(TToken);
+end;
+
+class procedure TWITScannerTests.AssertEquals(Msg: string; aExpected,
+  aActual: TTokenType);
+begin
+  AssertEquals(Msg,GetEnumName(TypeInfo(TTokenType),ord(aExpected)),
+                   GetEnumName(TypeInfo(TTokenType),ord(aActual)));
+end;
+
+procedure TWITScannerTests.TestUnknown;
+begin
+  TestToken('?', ttUnknown);
+end;
+
+procedure TWITScannerTests.TestWhitespace;
+begin
+  TestToken('  ', ttWhitespace);
+end;
+
+procedure TWITScannerTests.TestEOF;
+begin
+  TestToken('', ttEOF);
+end;
+
+procedure TWITScannerTests.TestOpenRoundBrace;
+begin
+  TestToken('(', ttOpenRoundBrace);
+end;
+
+procedure TWITScannerTests.TestCloseRoundBrace;
+begin
+  TestToken(')', ttCloseRoundBrace);
+end;
+
+procedure TWITScannerTests.TestOpenCurlyBrace;
+begin
+  TestToken('{', ttOpenCurlyBrace);
+end;
+
+procedure TWITScannerTests.TestCloseCurlyBrace;
+begin
+  TestToken('}', ttCloseCurlyBrace);
+end;
+
+procedure TWITScannerTests.TestOpenSquareBrace;
+begin
+  TestToken('[', ttOpenSquareBrace);
+end;
+
+procedure TWITScannerTests.TestCloseSquareBrace;
+begin
+  TestToken(']', ttCLoseSquareBrace);
+end;
+
+procedure TWITScannerTests.TestColon;
+begin
+  TestToken(':', ttColon);
+end;
+
+procedure TWITScannerTests.TestSemicolon;
+begin
+  TestToken(';', ttSemicolon);
+end;
+
+procedure TWITScannerTests.TestComma;
+begin
+  TestToken(',', ttComma);
+end;
+
+procedure TWITScannerTests.TestEqual;
+begin
+  TestToken('=', ttEqual);
+end;
+
+procedure TWITScannerTests.TestDot;
+begin
+  TestToken('.', ttDot);
+end;
+
+procedure TWITScannerTests.TestPlus;
+begin
+  TestToken('+', ttPlus);
+end;
+
+procedure TWITScannerTests.TestMinus;
+begin
+  TestToken('-', ttMinus);
+end;
+
+procedure TWITScannerTests.TestStar;
+begin
+  TestToken('*', ttStar);
+end;
+
+procedure TWITScannerTests.TestDiv;
+begin
+  TestToken('/', ttDiv);
+end;
+
+procedure TWITScannerTests.TestLessThan;
+begin
+  TestToken('<', ttLessThan);
+end;
+
+procedure TWITScannerTests.TestGreaterThan;
+begin
+  TestToken('>', ttGreaterThan);
+end;
+
+procedure TWITScannerTests.TestAt;
+begin
+  TestToken('@', ttAt);
+end;
+
+procedure TWITScannerTests.TestArrow;
+begin
+  TestToken('->', ttArrow);
+end;
+
+procedure TWITScannerTests.TestNumber;
+begin
+  TestToken('123', ttNumber);
+end;
+
+procedure TWITScannerTests.TestString;
+begin
+  TestToken('"test"', ttStringLiteral);
+end;
+
+procedure TWITScannerTests.TestInterface;
+begin
+  TestToken('interface', ttInterface);
+end;
+
+procedure TWITScannerTests.TestRecord;
+begin
+  TestToken('record', ttRecord);
+end;
+
+procedure TWITScannerTests.TestFlags;
+begin
+  TestToken('flags', ttFlags);
+end;
+
+procedure TWITScannerTests.TestEnum;
+begin
+  TestToken('enum', ttEnum);
+end;
+
+procedure TWITScannerTests.TestType;
+begin
+  TestToken('type', ttType);
+end;
+
+procedure TWITScannerTests.TestResource;
+begin
+  TestToken('resource', ttResource);
+end;
+
+procedure TWITScannerTests.TestImport;
+begin
+  TestToken('import', ttImport);
+end;
+
+procedure TWITScannerTests.TestExport;
+begin
+  TestToken('export', ttExport);
+end;
+
+procedure TWITScannerTests.TestUse;
+begin
+  TestToken('use', ttUse);
+end;
+
+procedure TWITScannerTests.TestWorld;
+begin
+  TestToken('world', ttWorld);
+end;
+
+procedure TWITScannerTests.TestPackage;
+begin
+  TestToken('package', ttPackage);
+end;
+
+procedure TWITScannerTests.TestAs;
+begin
+  TestToken('as', ttAs);
+end;
+
+procedure TWITScannerTests.TestAsync;
+begin
+  TestToken('async', ttAsync);
+end;
+
+procedure TWITScannerTests.TestBool;
+begin
+  TestToken('bool', ttBool);
+end;
+
+procedure TWITScannerTests.TestBorrow;
+begin
+  TestToken('borrow', ttBorrow);
+end;
+
+procedure TWITScannerTests.TestChar;
+begin
+  TestToken('char', ttChar);
+end;
+
+procedure TWITScannerTests.TestConstructor;
+begin
+  TestToken('constructor', ttConstructor);
+end;
+
+procedure TWITScannerTests.TestFrom;
+begin
+  TestToken('from', ttFrom);
+end;
+
+procedure TWITScannerTests.TestFunc;
+begin
+  TestToken('func', ttFunc);
+end;
+
+procedure TWITScannerTests.TestFuture;
+begin
+  TestToken('future', ttFuture);
+end;
+
+procedure TWITScannerTests.TestInclude;
+begin
+  TestToken('include', ttInclude);
+end;
+
+procedure TWITScannerTests.TestList;
+begin
+  TestToken('list', ttList);
+end;
+
+procedure TWITScannerTests.TestOption;
+begin
+  TestToken('option', ttOption);
+end;
+
+procedure TWITScannerTests.TestOwn;
+begin
+  TestToken('own', ttOwn);
+end;
+
+procedure TWITScannerTests.TestResult;
+begin
+  TestToken('result', ttResult);
+end;
+
+procedure TWITScannerTests.TestStatic;
+begin
+  TestToken('static', ttStatic);
+end;
+
+procedure TWITScannerTests.TestStream;
+begin
+  TestToken('stream', ttStream);
+end;
+
+procedure TWITScannerTests.TestStringKW;
+begin
+  TestToken('string', ttStringType);
+end;
+
+procedure TWITScannerTests.TestTuple;
+begin
+  TestToken('tuple', ttTuple);
+end;
+
+procedure TWITScannerTests.TestVariant;
+begin
+  TestToken('variant', ttVariant);
+end;
+
+procedure TWITScannerTests.TestWith;
+begin
+  TestToken('with', ttWith);
+end;
+
+procedure TWITScannerTests.TestIdentifier;
+begin
+  TestToken('myIdentifier', ttIdentifier);
+end;
+
+procedure TWITScannerTests.TestIdentifierEscapedKeyword;
+begin
+  TestToken('%with', ttIdentifier);
+  AssertEquals('Escape not part of token','with',FToken.Value);
+end;
+
+procedure TWITScannerTests.TestIdentifierDash;
+begin
+  TestToken('ali-baba', ttIdentifier);
+  AssertEquals('dash part of token','ali-baba',FToken.Value);
+end;
+
+
+procedure TWITScannerTests.TestSingleComment;
+begin
+  TestToken('// A comment', ttComment);
+  AssertEquals('Comment',' A comment',FToken.Value);
+end;
+
+procedure TWITScannerTests.TestMultiComment;
+begin
+  TestToken('/* A comment*/', ttComment);
+  AssertEquals('Comment',' A comment',FToken.Value);
+end;
+
+procedure TWITScannerTests.TestMultiComment2;
+begin
+  TestToken('/* A'#10'comment*/', ttComment);
+  AssertEquals('Comment',' A'#10'comment',FToken.Value);
+end;
+
+procedure TWITScannerTests.TestMultiCommentNested;
+begin
+  TestToken('/* A /* second */ comment*/', ttComment);
+  AssertEquals('Comment',' A /* second */ comment',FToken.Value);
+end;
+
+initialization
+  RegisterTest(TWITScannerTests);
+
+end.
+

+ 1 - 0
packages/fpmake_add.inc

@@ -162,6 +162,7 @@
   add_wasm_oi(ADirectory+IncludeTrailingPathDelimiter('wasm-oi'));
   add_fcl_jsonschema(ADirectory+IncludeTrailingPathDelimiter('fcl-jsonschema'));
   add_fcl_openapi(ADirectory+IncludeTrailingPathDelimiter('fcl-openapi'));
+  add_fcl_wit(ADirectory+IncludeTrailingPathDelimiter('fcl-wit'));
   add_fcl_yaml(ADirectory+IncludeTrailingPathDelimiter('fcl-yaml'));
   add_ptckvm(ADirectory+IncludeTrailingPathDelimiter('ptckvm'));
   add_fcl_fpterm(ADirectory+IncludeTrailingPathDelimiter('fcl-fpterm'));

+ 6 - 0
packages/fpmake_proc.inc

@@ -918,6 +918,12 @@ begin
 {$include fcl-openapi/fpmake.pp}
 end;
 
+procedure add_fcl_wit(const ADirectory: string);
+begin
+  with Installer do
+{$include fcl-wit/fpmake.pp}
+end;
+
 procedure add_fcl_yaml(const ADirectory: string);
 begin
   with Installer do