unit HeaderGenerator; {$mode objfpc}{$H+} interface uses Classes, SysUtils, DOM, uutlGenerics, variants; type //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// THeaderGenerator = class(TObject) private type TStringTree = class(TObject) public type TItemList = specialize TutlList; TItemMap = specialize TutlMap; private fLinesBefore: TStringList; fLinesAfter: TStringList; fItemList: TItemList; fItemMap: TItemMap; function GetItem(const aName: String): TStringTree; function GetItemCount: Integer; function GetItemsByIndex(const aIndex: Integer): TStringTree; function GetLinesAfter: TStrings; function GetLinesBefore: TStrings; function GetText: String; public property Text: String read GetText; property LinesBefore: TStrings read GetLinesBefore; property LinesAfter: TStrings read GetLinesAfter; property ItemCount: Integer read GetItemCount; property Items [const aName: String]: TStringTree read GetItem; property ItemsByIndex[const aIndex: Integer]: TStringTree read GetItemsByIndex; function IndexOf(const aItem: TStringTree): Integer; function GetOrCreateItem(const aName: String): TStringTree; function CreateItemAt(const aName: String; const aIndex: Integer): TStringTree; function DestroyItem(const aName: String): Boolean; procedure Clear; constructor Create; destructor Destroy; override; end; //////////////////////////////////////////////////////////////////////////////////////////// TvkObjectType = ( otUnknown, otType, otHandle, otEnum, otConst, otFuncPtr, otStruct, otCommand); TvkObjectTypes = set of TvkObjectType; TvkObject = class; TvkObjectList = class(specialize TutlList) public function IndexOfObj(const aName: DOMstring; const aTypes: TvkObjectTypes): Integer; function FindObj(const aName: DOMstring; const aTypes: TvkObjectTypes; out aObject): Boolean; function GetPascalName(const aName: DOMString; const aPointer: Integer; const aArrSize: WideString = ''): WideString; function GenerateCode(const aName: String; const aCode: TStringTree): Boolean; procedure AddObject(const aObj: TvkObject); end; //////////////////////////////////////////////////////////////////////////////////////////// TvkObject = class(TObject) private fType: TvkObjectType; fName: DOMString; fComment: DOMString; fList: TvkObjectList; protected function GetDependencyOrder: Integer; virtual; procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); virtual; {%H-}constructor Create(const aType: TvkObjectType); public property VkType: TvkObjectType read fType; property Name: DOMString read fName write fName; property Comment: DOMString read fComment write fComment; property List: TvkObjectList read fList write fList; function GetPascalName(const aPointer: Integer; const aArrSize: WideString): WideString; procedure ReadXml(const xml: TDOMNode); virtual; procedure GenerateCode(const aCode: TStringTree); virtual; public class procedure CreateFromXml(const xml: TDOMNode; const aList: TvkObjectList); end; //////////////////////////////////////////////////////////////////////////////////////////// TvkTypeBase = class(TvkObject) private fParent: DOMString; fAlias: DOMString; protected function GetDependencyOrder: Integer; override; procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); override; public property Parent: DOMString read fParent write fParent; property Alias: DOMString read fAlias write fAlias; procedure GenerateCode(const aCode: TStringTree); override; end; //////////////////////////////////////////////////////////////////////////////////////////// TvkType = class(TvkTypeBase) public procedure ReadXml(const xml: TDOMNode); override; constructor Create; end; //////////////////////////////////////////////////////////////////////////////////////////// TvkHandle = class(TvkTypeBase) public procedure ReadXml(const xml: TDOMNode); override; constructor Create; end; //////////////////////////////////////////////////////////////////////////////////////////// TvkFunctionBase = class(TvkObject) private type TArgument = class(TObject) Typ: DOMString; Name: DOMString; IsPointer: Integer; IsConst: Boolean; end; TArgumentList = specialize TutlList; private fReturnValue: TArgument; fArguments: TArgumentList; protected function GetDependencyOrder: Integer; override; procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); override; procedure GenerateFunctionPtr(const aCode, aSub: TStringTree); {%H-}constructor Create(const aType: TvkObjectType); public property ReturnValue: TArgument read fReturnValue; property Arguments: TArgumentList read fArguments; destructor Destroy; override; end; //////////////////////////////////////////////////////////////////////////////////////////// TvkFunctionPtr = class(TvkFunctionBase) public procedure ReadXml(const xml: TDOMNode); override; procedure GenerateCode(const aCode: TStringTree); override; constructor Create; end; //////////////////////////////////////////////////////////////////////////////////////////// TvkCommand = class(TvkFunctionBase) public procedure ReadXml(const xml: TDOMNode); override; procedure GenerateCode(const aCode: TStringTree); override; constructor Create; end; //////////////////////////////////////////////////////////////////////////////////////////// TvkStruct = class(TvkObject) private type TMember = class(TObject) Typ: DOMString; Name: DOMString; ArrSize: DOMString; IsPointer: Integer; IsConst: Boolean; end; TMemberList = specialize TutlList; private fIsUnion: Boolean; fMember: TMemberList; protected function GetDependencyOrder: Integer; override; procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); override; public property Member: TMemberList read fMember; property IsUnion: Boolean read fIsUnion write fIsUnion; procedure ReadXml(const xml: TDOMNode); override; procedure GenerateCode(const aCode: TStringTree); override; constructor Create; destructor Destroy; override; end; //////////////////////////////////////////////////////////////////////////////////////////// TvkEnum = class(TvkObject) private type TValue = class(TObject) Name: DOMString; Value: Variant; IsBit: Boolean; end; TValueList = specialize TutlList; private fExpands: DOMString; fValues: TValueList; protected procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); override; public property Expands: DOMString read fExpands write fExpands; property Values: TValueList read fValues; procedure ReadXml(const xml: TDOMNode); override; procedure GenerateCode(const aCode: TStringTree); override; constructor Create; destructor Destroy; override; end; //////////////////////////////////////////////////////////////////////////////////////////// TvkConst = class(TvkObject) private fValue: Variant; public property Value: Variant read fValue write fValue; procedure GenerateCode(const aCode: TStringTree); override; constructor Create; end; private const VK_OBJECT_TYPES: TvkObjectTypes = [ otType, otHandle, otFuncPtr, otStruct, otEnum ]; VK_OBJECT_ALL: TvkObjectTypes = [ low(TvkObjectType) .. high(TvkObjectType) ]; private fDocument: TXMLDocument; fObjects: TvkObjectList; fComment: WideString; public procedure ReadFile(const aFilename: String); procedure ReadFile(const aStream: TStream); procedure GenerateCode(const aName: WideString; const aTemplate: String; const aOutput: String); procedure Clear; constructor Create; destructor Destroy; override; end; implementation uses Math, XMLRead, RegExpr; type XmlException = class(Exception) public xml: TDOMNode; constructor Create(const aMsg: WideString; const aXml: TDOMNode); end; const KEYWORD_INTF = 'INTERFACE'; KEYWORD_CONST = 'CONST'; KEYWORD_TYPES = 'TYPES'; KEYWORD_ENUM = 'ENUM'; KEYWORD_ENUM_VALUES = 'ENUM_VALUES'; KEYWORD_STRUCT = 'STRUCT'; KEYWORD_FUNCPTR = 'FUNCPTR'; KEYWORD_COMMAND = 'COMMAND'; KEYWORD_IMPL = 'IMPLEMENTATION'; KEYWORD_COMMENT = 'COMMENT'; KEYWORD_VAR = 'VAR'; KEYWORD_CODE = 'CODE'; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function PointerCount(const aStr: AnsiString): Integer; var i: Integer; begin result := 0; i := Length(aStr); while (i > 0) do begin if (aStr[i] = '*') then inc(result); dec(i); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function StrToValue(s: String): Variant; var f: TFormatSettings; rx: TRegExpr; begin f.DecimalSeparator := '.'; rx := TRegExpr.Create; try result := Unassigned; rx.Expression := '^-?([0-9]+)$'; if rx.Exec(s) then result := StrToInt(rx.Match[0]); rx.Expression := '^0x([0-9A-Fa-f]+)$'; if rx.Exec(s) then result := '$' + rx.Match[1]; rx.Expression := '^([0-9\.]+)(f|d)?$'; if rx.Exec(s) then begin result := StrToFloat(rx.Match[1], f); if (rx.Match[2] = 'd') then result := Double(result); end; rx.Expression := '^\(~0U([L]*)\)$'; if rx.Exec(s) then begin if (rx.Match[2] = '') then result := '$' + IntToHex(High(UInt32), 2 * sizeof(UInt32)) else result := '$' + IntToHex(High(UInt64), 2 * sizeof(UInt64)); end; finally FreeAndNil(rx); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function PrepareName(const aName, aType: WideString): WideString; var rx: TRegExpr; s: WideString; begin result := aName; s := ''; if (result = 'type') then s := 'Type' else if (result = 'object') then s := 'Object'; if (s <> '') then begin rx := TRegExpr.Create('^(T|P)?(Vk)?(.)'); try if rx.Exec(String(aType)) then result := WideString(LowerCase(rx.Match[3]) + s) else result := 'm' + s; finally FreeAndNil(rx); end; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function FormatType(const aType: THeaderGenerator.TvkObjectType): WideString; begin case aType of otType: result := 'TYPE'; otHandle: result := 'HANDLE'; otFuncPtr: result := 'FUNCPTR'; otStruct: result := 'STRUCT'; otEnum: result := 'ENUM'; otConst: result := 'CONSTANT'; otCommand: result := 'COMMAND'; else result := ''; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure WriteResolveWarning(const aName: DOMString); begin WriteLn('WARNING! unable to resolve identifier: ' + aName); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetNodeValue(xml: TDOMNode; const aName: WideString): DOMString; var x: TDOMNode; begin x := xml.FindNode(aName); if not Assigned(x) then raise XmlException.Create(xml.NodeName + ' without child ''' + aName + '''', xml); result := x.TextContent; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TryGetNodeValue(xml: TDOMNode; const aName: WideString; out aValue: DOMString): Boolean; var x: TDOMNode; begin x := xml.FindNode(aName); result := Assigned(x); if result then aValue := x.TextContent; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetNodeValue(xml: TDOMNode; const aName: WideString; const aDefault: DOMString): DOMString; var x: TDOMNode; begin x := xml.FindNode(aName); if not Assigned(x) then result := aDefault else result := x.TextContent; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetNodeAttrib(xml: TDOMNode; const aName: WideString): DOMString; var x: TDOMNode; begin if xml.HasAttributes then begin x := xml.Attributes.GetNamedItem(aName); if Assigned(x) then begin result := x.TextContent; exit; end; end; raise XmlException.Create(xml.NodeName + ' without ''' + aName + ''' attribute', xml); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TryGetNodeAttrib(xml: TDOMNode; const aName: WideString; out aValue: DOMString): Boolean; var x: TDOMNode; begin result := true; if xml.HasAttributes then begin x := xml.Attributes.GetNamedItem(aName); if Assigned(x) then begin aValue := x.TextContent; exit; end; end; result := false; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetNodeAttrib(xml: TDOMNode; const aName: WideString; const aDefault: DOMString): DOMString; var x: TDOMNode; begin result := aDefault; if xml.HasAttributes then begin x := xml.Attributes.GetNamedItem(aName); if Assigned(x) then result := x.TextContent; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //XmlException////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor XmlException.Create(const aMsg: WideString; const aXml: TDOMNode); begin inherited Create(String(aMsg + ': ' + aXml.TextContent)); xml := aXml; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TStringTree////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.GetLinesAfter: TStrings; begin result := fLinesAfter; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.GetLinesBefore: TStrings; begin result := fLinesBefore; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.GetItem(const aName: String): TStringTree; begin result := fItemMap[aName]; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.GetItemCount: Integer; begin result := fItemList.Count; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.GetItemsByIndex(const aIndex: Integer): TStringTree; begin result := fItemList[aIndex]; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.GetText: String; var i: Integer; s: String; procedure AddText(const aText: String; const aCanAddEmpty: Boolean); begin if not aCanAddEmpty and (aText = '') then exit; result := result + aText + sLineBreak; end; begin result := ''; for i := 0 to fLinesBefore.Count-1 do AddText(fLinesBefore[i], true); for i := 0 to fItemList.Count-1 do begin s := fItemList[i].Text; if (UpperCase(Trim(s)) = '{$ENDIF}') then result := TrimRight(result) + sLineBreak; AddText(s, false); end; for i := 0 to fLinesAfter.Count-1 do AddText(fLinesAfter[i], true); result := copy(result, 0, Length(result) - Length(sLineBreak)); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.IndexOf(const aItem: TStringTree): Integer; begin result := fItemList.IndexOf(aItem); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.GetOrCreateItem(const aName: String): TStringTree; begin result := GetItem(aName); if not Assigned(result) then begin result := TStringTree.Create; fItemMap.Add(aName, result); fItemList.Add(result); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.CreateItemAt(const aName: String; const aIndex: Integer): TStringTree; begin result := TStringTree.Create; fItemMap.Add(aName, result); fItemList.Insert(aIndex, result); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TStringTree.DestroyItem(const aName: String): Boolean; var t: TStringTree; begin t := GetItem(aName); result := Assigned(t); if result then begin fItemList.Remove(t); fItemMap.Delete(aName); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TStringTree.Clear; begin fItemList.Clear; fItemMap.Clear; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TStringTree.Create; begin inherited Create; fLinesBefore := TStringList.Create; fLinesAfter := TStringList.Create; fItemList := TItemList.Create(false); fItemMap := TItemMap.Create(true); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// destructor THeaderGenerator.TStringTree.Destroy; begin FreeAndNil(fItemList); FreeAndNil(fItemMap); FreeAndNil(fLinesBefore); FreeAndNil(fLinesAfter); inherited Destroy; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkObjectList//////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TvkObjectList.IndexOfObj(const aName: DOMstring; const aTypes: TvkObjectTypes): Integer; var o: TvkObject; begin if (aName = '') then raise Exception.Create('empty name'); for result := 0 to Count-1 do begin o := GetItem(result); if (o.VkType in aTypes) and (o.Name = aName) then exit; end; result := -1; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TvkObjectList.FindObj(const aName: DOMstring; const aTypes: TvkObjectTypes; out aObject): Boolean; var i: Integer; begin i := IndexOfObj(aName, aTypes); result := (i >= 0); if result then TvkObject(aObject) := GetItem(i) else TvkObject(aObject) := nil; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TvkObjectList.GetPascalName(const aName: DOMString; const aPointer: Integer; const aArrSize: WideString): WideString; var o: TvkObject; begin if FindObj(aName, VK_OBJECT_TYPES, o) then result := o.GetPascalName(aPointer, aArrSize) else result := aName; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TvkObjectList.GenerateCode(const aName: String; const aCode: TStringTree): Boolean; var o: TvkObject; begin result := FindObj(aName, VK_OBJECT_ALL, o); if result then o.GenerateCode(aCode); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkObjectList.AddObject(const aObj: TvkObject); begin if Assigned(aObj) then begin Add(aObj); aObj.List := self; WriteLn(Format(' %-10s%s', [FormatType(aObj.VkType), aObj.Name])); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkObject//////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TvkObject.GetDependencyOrder: Integer; begin result := 0; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkObject.GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); var i: Integer; begin if (aName = '') then begin aName := fName; if (aName = 'void') then aName := 'VkVoid'; for i := 0 to aPointer-1 do aName := 'P' + aName; end; if (aName = 'VkVoid') then raise Exception.Create('void can not be used as type'); if (aArrSize <> '') then begin if TryStrToInt(String(aArrSize), i) then aName := WideString(Format('array[0..%d] of %s', [i, aName])) else aName := WideString(Format('array[0..%s-1] of %s', [aArrSize, aName])); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TvkObject.Create(const aType: TvkObjectType); begin inherited Create; fType := aType; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TvkObject.GetPascalName(const aPointer: Integer; const aArrSize: WideString): WideString; begin result := ''; GenPascalName(result, aPointer, aArrSize); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkObject.ReadXml(const xml: TDOMNode); begin // DUMMY end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkObject.GenerateCode(const aCode: TStringTree); begin // DUMMY end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class procedure THeaderGenerator.TvkObject.CreateFromXml(const xml: TDOMNode; const aList: TvkObjectList); var s, n: DOMString; procedure GenerateVersionEnum; var c: TvkConst; rx: TRegExpr; begin rx := TRegExpr.Create('\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)'); try if not rx.Exec(String(xml.TextContent)) then raise XmlException.Create('VK_API_VERSION with malformed version', xml); c := TvkConst.Create; try c.Name := 'VK_API_VERSION'; c.Value := (StrToInt(rx.Match[1]) shl 22) or (StrToInt(rx.Match[2]) shl 12) or (StrToInt(rx.Match[3]) shl 0); aList.AddObject(c); except FreeAndNil(c); raise end; c := TvkConst.Create; try c.Name := 'VK_API_VERSION_MAJOR'; c.Value := StrToInt(rx.Match[1]); aList.AddObject(c); except FreeAndNil(c); raise end; c := TvkConst.Create; try c.Name := 'VK_API_VERSION_MINOR'; c.Value := StrToInt(rx.Match[2]); aList.AddObject(c); except FreeAndNil(c); raise end; c := TvkConst.Create; try c.Name := 'VK_API_VERSION_BUGFIX'; c.Value := StrToInt(rx.Match[3]); aList.AddObject(c); except FreeAndNil(c); raise end; finally FreeAndNil(rx); end; end; procedure GenerateConstants; var x: TDOMNode; c: TvkConst; begin x := xml.FirstChild; while Assigned(x) do begin if (x.NodeName = 'enum') then begin c := TvkConst.Create; try c.Name := GetNodeAttrib(x, 'name'); c.Value := StrToValue(String(GetNodeAttrib(x, 'value'))); if (c.Value = Unassigned) then raise Exception.Create('Invalid constant value: ' + GetNodeAttrib(x, 'value')); aList.AddObject(c); except FreeAndNil(c); raise; end; end; x := x.NextSibling; end; end; var obj: TvkObject; begin try obj := nil; if (xml.NodeName = 'type') and TryGetNodeAttrib(xml, 'category', s) then begin if (s = 'basetype') or (s = 'bitmask') then obj := TvkType.Create else if (s = 'handle') then obj := TvkHandle.Create else if (s = 'funcpointer') then obj := TvkFunctionPtr.Create else if (s = 'struct') or (s = 'union') then obj := TvkStruct.Create else if (s = 'define') and TryGetNodeValue(xml, 'name', n) and (n = 'VK_API_VERSION') then GenerateVersionEnum; end else if (xml.NodeName = 'command') then obj := TvkCommand.Create else if (xml.NodeName = 'enums') then begin if not TryGetNodeAttrib(xml, 'type', s) then GenerateConstants else if (s = 'enum') or (s = 'bitmask') then obj := TvkEnum.Create; end; if Assigned(obj) then begin obj.ReadXml(xml); aList.AddObject(obj); end; except FreeAndNil(obj); raise; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkTypeBase////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TvkTypeBase.GetDependencyOrder: Integer; begin result := inherited GetDependencyOrder; if Assigned(List) and (fParent <> '') then result := max(result, List.IndexOfObj(fParent, VK_OBJECT_TYPES) + 1); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkTypeBase.GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); var i: Integer; begin if (fAlias <> '') then aName := fAlias else aName := fName; if (aName = 'void') then aName := 'VkVoid'; for i := 0 to aPointer-1 do aName := 'P' + aName; if (aName = 'VkVoid') then raise Exception.Create('void can not be used as type'); inherited GenPascalName(aName, aPointer, aArrSize); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkTypeBase.GenerateCode(const aCode: TStringTree); var st: TStringTree; begin inherited GenerateCode(aCode); if Assigned(fList) and (fParent <> '') then fList.GenerateCode(fParent, aCode); if Assigned(fList) and (fAlias <> '') then fList.GenerateCode(fAlias, aCode); st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_TYPES); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add('type { simple types }'); st := st.GetOrCreateItem(fName); if (st.LinesBefore.Count = 0) and (fParent <> '') and Assigned(List) then begin st.LinesBefore.Add(Format(' %-62s = %s;', [ fName, List.GetPascalName(fParent, 0) ])); st.LinesBefore.Add(Format(' P%-61s = ^%0:s;', [ fName ])); st.LinesBefore.Add(Format(' PP%-60s = ^P%0:s;', [ fName ])); st.LinesBefore.Add(''); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkType////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkType.ReadXml(const xml: TDOMNode); begin inherited ReadXml(xml); Name := GetNodeValue(xml, 'name'); Parent := GetNodeValue(xml, 'type'); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TvkType.Create; begin inherited Create(otType); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkHandle//////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TvkHandle.Create; begin inherited Create(otHandle); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkHandle.ReadXml(const xml: TDOMNode); begin inherited ReadXml(xml); Name := GetNodeValue (xml, 'name'); //Parent := GetNodeAttrib(xml, 'parent', 'VkHandle'); Parent := 'VkHandle'; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TvkFunctionBase.GetDependencyOrder: Integer; var a: TArgument; begin result := inherited GetDependencyOrder; if not Assigned(List) then exit; for a in fArguments do result := max(result, List.IndexOfObj(a.Typ, VK_OBJECT_TYPES)); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkFunctionBase.GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); var rx: TRegExpr; begin if (aPointer > 0) then Exception.Create('function pointer can not be used as pointer type'); if (aArrSize <> '') then Exception.Create('function pointer can not be used as array type'); rx := TRegExpr.Create('PFN_([-_A-Za-z0-9]+)'); try if rx.Exec(String(fName)) then aName := 'T' + WideString(rx.Match[1]) else aName := 'T' + fName; finally FreeAndNil(rx); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkFunctionBase.GenerateFunctionPtr(const aCode, aSub: TStringTree); var st: TStringTree; i: Integer; s: WideString; a: TArgument; begin if Assigned(fList) then begin fList.GenerateCode(fReturnValue.Typ, aCode); for a in fArguments do fList.GenerateCode(a.Typ, aCode); end; st := aSub.GetOrCreateItem(GetPascalName(0, '')); if (st.LinesBefore.Count = 0) then begin // head s := WideString(Format(' %s', [ GetPascalName(0, '') ])); if (ReturnValue.Typ = 'void') and (ReturnValue.IsPointer = 0) then s := s + ' = procedure(' else s := s + ' = function ('; st.LinesBefore.Add(s); // params for i := 0 to fArguments.Count-1 do begin s := ' '; a := fArguments[i]; if a.IsConst then s := s + 'const ' else s := s + ' '; s := s + Format('%-30s%s', [ PrepareName(a.Name, a.Typ)+':', List.GetPascalName(a.Typ, a.IsPointer) ]); if (i < fArguments.Count-1) then s := s + '; '; st.LinesBefore.Add(s); end; // result s := ' )'; if (ReturnValue.Typ <> 'void') or (ReturnValue.IsPointer > 0) then s := s + ': ' + List.GetPascalName(ReturnValue.Typ, ReturnValue.IsPointer); s := s + '; {$IFDEF VK_CDECL}cdecl{$ELSE}stdcall{$ENDIF};'; st.LinesBefore.Add(s); st.LinesBefore.Add(''); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TvkFunctionBase.Create(const aType: TvkObjectType); begin inherited Create(aType); fReturnValue := TArgument.Create; fArguments := TArgumentList.Create; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// destructor THeaderGenerator.TvkFunctionBase.Destroy; begin FreeAndNil(fReturnValue); FreeAndNil(fArguments); inherited Destroy; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkFunctionPtr/////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkFunctionPtr.ReadXml(const xml: TDOMNode); var x, p: TDOMNode; a: TArgument; rx: TRegExpr; begin x := xml.FindNode('name'); if not Assigned(x) then raise XmlException.Create(xml.NodeName + ' witout child ''name''', xml); Name := x.TextContent; p := x.PreviousSibling; if not Assigned(p) then raise XmlException.Create(xml.NodeName + ' witout return type', xml); rx := TRegExpr.Create('typedef\s+((const)?)\s*([-_A-Za-z0-9\s]+?)\s*((\*\s*(const)?\s*)*)\s*\('); try if not rx.Exec(String(p.TextContent)) then raise XmlException.Create('unable to parse return type of function pointer', xml); ReturnValue.IsConst := (Trim(rx.Match[1]) = 'const'); ReturnValue.Typ := WideString(Trim(rx.Match[3])); ReturnValue.IsPointer := PointerCount(Trim(rx.Match[4])); x := xml.FirstChild; while Assigned(x) do begin if (x.NodeName = 'type') then begin a := TArgument.Create; try p := x.PreviousSibling; if not Assigned(p) then raise XmlException.Create('unable to parse function pointer arguments', xml); rx.Expression := '[A-Za-z]*'; a.IsConst := rx.Exec(String(p.TextContent)) and (rx.Match[0] = 'const'); a.Typ := x.TextContent; p := x.NextSibling; if not Assigned(p) then raise XmlException.Create('unable to parse function pointer arguments', xml); rx.Expression := '((\*\s*(const)?\s*)*)\s*([-_A-Za-z0-9]+)'; if not rx.Exec(String(p.TextContent)) then raise XmlException.Create('unable to parse function pointer arguments', xml); a.IsPointer := PointerCount((rx.Match[1])); a.Name := WideString(Trim(rx.Match[4])); Arguments.Add(a); except FreeAndNil(a) end; end; x := x.NextSibling; end; finally FreeAndNil(rx); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkFunctionPtr.GenerateCode(const aCode: TStringTree); var st: TStringTree; begin inherited GenerateCode(aCode); st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_FUNCPTR); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add('type { function pointers }'); GenerateFunctionPtr(aCode, st); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TvkFunctionPtr.Create; begin inherited Create(otFuncPtr); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkCommand/////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkCommand.ReadXml(const xml: TDOMNode); procedure ParseProto(const x: TDOMNode); begin Name := GetNodeValue(x, 'name'); ReturnValue.Typ := GetNodeValue(x, 'type'); end; procedure ParseParam(const x: TDOMNode); var p: TDOMNode; a: TArgument; rx: TRegExpr; begin a := TArgument.Create; rx := TRegExpr.Create; try try p := x.FindNode('name'); if not Assigned(p) then raise XmlException.Create('unable to parse command; param without name', xml); a.Name := p.TextContent; p := x.FindNode('type'); if not Assigned(p) then raise XmlException.Create('unable to parse command; param without type', xml); a.Typ := p.TextContent; a.IsConst := Assigned(p.PreviousSibling) and (Trim(p.PreviousSibling.TextContent) = 'const'); rx.Expression := '((\*\s*(const)?\s*)*)'; if Assigned(p.NextSibling) and rx.Exec(String(p.NextSibling.TextContent)) then a.IsPointer := PointerCount(rx.Match[0]); Arguments.Add(a); finally FreeAndNil(rx); end; except FreeAndNil(a); raise; end; end; var x: TDOMNode; begin inherited ReadXml(xml); x := xml.FirstChild; while Assigned(x) do begin if (x.NodeName = 'proto') then ParseProto(x) else if (x.NodeName = 'param') then ParseParam(x); x := x.NextSibling; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkCommand.GenerateCode(const aCode: TStringTree); var st: TStringTree; begin inherited GenerateCode(aCode); st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_COMMAND); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add('type { commands }'); GenerateFunctionPtr(aCode, st); st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_VAR); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add('var'); st := st.GetOrCreateItem(fName); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add(Format(' %-50s%s;', [fName + ':', GetPascalName(0, '')])); st := aCode.GetOrCreateItem(KEYWORD_IMPL).GetOrCreateItem(KEYWORD_CODE).GetOrCreateItem(fName); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add(Format(' %-50s := T%0:-50s(vkGetProcAddress(%-50saInstance));', [fName, '''' + fName + ''','])); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TvkCommand.Create; begin inherited Create(otCommand); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkStruct//////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function THeaderGenerator.TvkStruct.GetDependencyOrder: Integer; var m: TMember; begin result := inherited GetDependencyOrder; if not Assigned(List) then exit; for m in fMember do result := max(result, List.IndexOfObj(m.Typ, VK_OBJECT_TYPES) + 1); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkStruct.GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); var i: Integer; begin if (aPointer > 0) then begin aName := fName; for i := 0 to aPointer-1 do aName := 'P' + aName; end else aName := 'T' + fName; inherited GenPascalName(aName, aPointer, aArrSize); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkStruct.ReadXml(const xml: TDOMNode); var x, p: TDOMNode; m: TMember; rx: TRegExpr; begin Name := GetNodeAttrib(xml, 'name'); IsUnion := (GetNodeAttrib(xml, 'category') = 'union'); x := xml.FirstChild; while Assigned(x) do begin if (x.NodeName = 'member') then begin m := TMember.Create; rx := TRegExpr.Create(''); try try // read name p := x.FindNode('name'); if not Assigned(p) then raise XmlException.Create('struct member without name', xml); m.Name := p.TextContent; p := p.NextSibling; while Assigned(p) do begin m.Name := m.Name + p.TextContent; p := p.NextSibling; end; m.Name := Trim(m.Name); rx.Expression := '([-_A-Za-z0-9]+)\s*\[\s*([-_A-Za-z0-9]+)\s*\]'; if rx.Exec(String(m.Name)) then begin m.Name := WideString(rx.Match[1]); m.ArrSize := WideString(rx.Match[2]); end; // read type p := x.FindNode('type'); if not Assigned(p) then raise XmlException.Create('struct member without type', xml); m.Typ := p.TextContent; m.IsConst := Assigned(p.PreviousSibling) and (Trim(p.PreviousSibling.TextContent) = 'const'); rx.Expression := '((\*\s*(const)?\s*)*)'; if Assigned(p.NextSibling) and rx.Exec(String(p.NextSibling.TextContent)) then m.IsPointer := PointerCount(rx.Match[0]); Member.Add(m); finally FreeAndNil(rx); end; except FreeAndNil(m); end; end; x := x.NextSibling; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkStruct.GenerateCode(const aCode: TStringTree); var m: TMember; st: TStringTree; postfix, prefix: String; i, indent: Integer; begin inherited GenerateCode(aCode); if Assigned(fList) then begin for m in fMember do begin if (m.ArrSize <> '') then fList.GenerateCode(m.ArrSize, aCode); fList.GenerateCode(m.Typ, aCode); end; end; st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_STRUCT); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add('type { structures }'); st := st.GetOrCreateItem(GetPascalName(0, '')); if (st.LinesBefore.Count = 0) then begin postfix := ';'; indent := 50; st.LinesBefore.Add(' T%s = record', [ fName ]); if IsUnion then begin st.LinesBefore.Add(' case Integer of'); postfix := ');'; indent := 42; end; for i := 0 to fMember.Count-1 do begin m := fMember[i]; if IsUnion then prefix := Format(' %.3d: (', [ i ]) else prefix := ' '; st.LinesBefore.Add(Format('%s%-*s%s%s', [ prefix, indent, PrepareName(m.Name, m.Typ) + ': ', List.GetPascalName(m.Typ, m.IsPointer, m.ArrSize), postfix ])); end; st.LinesAfter.Add(' end;'); st.LinesAfter.Add(' P%s = ^T%0:s;', [ fName ]); st.LinesAfter.Add(' PP%s = ^P%0:s;', [ fName ]); st.LinesAfter.Add(''); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TvkStruct.Create; begin inherited Create(otStruct); fMember := TMemberList.Create; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// destructor THeaderGenerator.TvkStruct.Destroy; begin FreeAndNil(fMember); inherited Destroy; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkEnum////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkEnum.GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); var i: Integer; begin if (fName = '') then raise Exception.Create('enum with empty name'); if (aPointer > 0) then begin aName := fName; for i := 0 to aPointer-1 do aName := 'P' + aName; end else aName := 'T' + fName; inherited GenPascalName(aName, aPointer, aArrSize); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkEnum.ReadXml(const xml: TDOMNode); var x, p: TDOMNode; v: TValue; begin Name := GetNodeAttrib(xml, 'name'); Expands := GetNodeAttrib(xml, 'expand', ''); x := xml.FirstChild; while Assigned(x) do begin if (x.NodeName = 'enum') then begin v := TValue.Create; try v.Name := GetNodeAttrib(x, 'name'); p := x.Attributes.GetNamedItem('value'); if Assigned(p) then begin v.IsBit := false; v.Value := StrToValue(String(p.TextContent)); end else begin p := x.Attributes.GetNamedItem('bitpos'); if not Assigned(p) then raise XmlException.Create('enum without value or bitpos', xml); v.IsBit := true; v.Value := StrToValue(String(p.TextContent)); end; if (v.Value = Unassigned) then raise XmlException.Create(WideString('invalid enum value (' + p.TextContent + ')'), xml); Values.Add(v); except FreeAndNil(v); raise; end; end; x := x.NextSibling; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkEnum.GenerateCode(const aCode: TStringTree); var st, vt: TStringTree; i: Integer; v: TValue; postfix: String; begin inherited GenerateCode(aCode); st := aCode .GetOrCreateItem(KEYWORD_INTF) .GetOrCreateItem(KEYWORD_ENUM); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add('type { enumerations }'); st := st.GetOrCreateItem(fName); if (st.LinesBefore.Count = 0) then begin st.LinesBefore.Add(Format(' %s = (', [GetPascalName(0, '')])); postfix := ','; vt := st.GetOrCreateItem(KEYWORD_ENUM_VALUES); for i := 0 to fValues.Count-1 do begin v := fValues[i]; if (i = fValues.Count-1) and (fExpands = '') then postfix := ''; if (v.IsBit) then vt.LinesBefore.Add(Format(' %-60s = (1 shl %s)%s', [v.Name, v.Value, postfix])) else vt.LinesBefore.Add(Format(' %-60s = %s%s', [v.Name, v.Value, postfix])); end; if (fExpands <> '') then st.LinesAfter.Add(Format(' %-60s = %s', [fExpands + '_MAX_ENUM', '$7FFFFFFF'])); st.LinesAfter.Add(' );'); st.LinesAfter.Add(' P%s = ^T%0:s;', [ fName ]); st.LinesAfter.Add(' PP%s = ^P%0:s;', [ fName ]); st.LinesAfter.Add(''); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TvkEnum.Create; begin inherited Create(otEnum); fValues := TValueList.Create; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// destructor THeaderGenerator.TvkEnum.Destroy; begin FreeAndNil(fValues); inherited Destroy; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator.TvkConst///////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.TvkConst.GenerateCode(const aCode: TStringTree); var st: TStringTree; begin inherited GenerateCode(aCode); st := aCode .GetOrCreateItem(KEYWORD_INTF) .GetOrCreateItem(KEYWORD_CONST); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add('const'); st := st.GetOrCreateItem(fName); if (st.LinesBefore.Count = 0) then st.LinesBefore.Add(Format(' %-62s = %s;', [fName, fValue])); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.TvkConst.Create; begin inherited Create(otConst); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //THeaderGenerator////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.GenerateCode(const aName: WideString; const aTemplate: String; const aOutput: String); type TPosMap = specialize TutlMap; var CurrentName: WideString; CurrentNumber: Integer; Code: TStringTree; PosMap: TPosMap; procedure UpdateComment(const aRoot: TStringTree; const aName: String); var i: Integer; s, tmp: String; st: TStringTree; begin st := aRoot.GetOrCreateItem(aName); if PosMap.TryGetValue(aName, i) and (st.ItemCount > i) and (CurrentName <> '') then with st.CreateItemAt(aName + CurrentName, i).LinesBefore do begin s := ' ' + CurrentName + ' '; if ((Length(s) and 1) = 1) then s := s + '='; tmp := StringOfChar('=', (100 - Length(s)) div 2); s := '{ ' + tmp + s + tmp + ' }'; Add(''); Add(s); end; PosMap[aName] := st.ItemCount; end; procedure UpdateComments; var st: TStringTree; begin st := Code.GetOrCreateItem(KEYWORD_INTF); UpdateComment(st, KEYWORD_CONST); UpdateComment(st, KEYWORD_TYPES); UpdateComment(st, KEYWORD_ENUM); UpdateComment(st, KEYWORD_FUNCPTR); UpdateComment(st, KEYWORD_STRUCT); UpdateComment(st, KEYWORD_COMMAND); UpdateComment(st, KEYWORD_VAR); st := Code.GetOrCreateItem(KEYWORD_IMPL); UpdateComment(st, KEYWORD_CODE); end; procedure AddGuard(const aTree: TStringTree; const aName: String); var i: Integer; begin for i := 0 to aTree.ItemCount-1 do AddGuard(aTree.ItemsByIndex[i], aName); with aTree.GetOrCreateItem('GUARD' + aName).LinesBefore do begin Add('{$IFDEF ' + aName + '}'); end; end; procedure RemoveGuard(const aTree: TStringTree; const aName: String); var i: Integer; g: TStringTree; begin for i := 0 to aTree.ItemCount-1 do RemoveGuard(aTree.ItemsByIndex[i], aName); g := aTree.GetOrCreateItem('GUARD' + aName); if (g = aTree.ItemsByIndex[aTree.ItemCount-1]) then aTree.DestroyItem('GUARD' + aName) else with aTree.GetOrCreateItem('GUARD_END'+ aName).LinesAfter do begin Add('{$ENDIF}'); Add(''); end; end; procedure CreateGuard(const aName: String); begin if (aName = '') then exit; AddGuard(Code, aName); end; procedure CloseGuard(const aName: String); begin if (aName = '') then exit; RemoveGuard(Code, aName); end; procedure GenerateFeatureCode(const aName: String); var i: Integer; begin i := fObjects.IndexOfObj(aName, VK_OBJECT_ALL); if (i >= 0) then fObjects[i].GenerateCode(Code); end; function GenerateEnum(const xml: TDOMNode): Boolean; var name: DOMString; ex: DOMString; value: DOMString; offset: Integer; s: String; c: TvkConst; begin result := false; name := GetNodeAttrib(xml, 'name'); if TryGetNodeAttrib(xml, 'extends', ex) then begin if (CurrentNumber <= 0) then raise Exception.Create('current extension has no number'); offset := StrToInt(GetNodeAttrib(xml, 'offset')); offset := 1000000000 + 1000 * (CurrentNumber-1) + offset; if (GetNodeAttrib(xml, 'dir', '') = '-') then offset := -1 * offset; Code .GetOrCreateItem(KEYWORD_INTF) .GetOrCreateItem(KEYWORD_ENUM) .GetOrCreateItem(ex) .GetOrCreateItem(KEYWORD_ENUM_VALUES) .LinesBefore.Add(Format(' %-60s = %s,', [name, IntToStr(offset)])); end else begin if not TryGetNodeAttrib(xml, 'value', value) then exit; s := StringReplace(string(value), '"', '''', [rfReplaceAll]); c := TvkConst.Create; try c.Name := name; c.Value := s; fObjects.AddObject(c); except FreeAndNil(c); raise; end; c.GenerateCode(Code); end; result := true; end; procedure GenerateFeatureCode(const xml: TDOMNode); var x: TDOMNode; begin x := xml.FirstChild; while Assigned(x) do begin if (x.NodeName = 'type') or (x.NodeName = 'command') then begin GenerateFeatureCode(GetNodeAttrib(x, 'name')); end else if (x.NodeName = 'enum') then begin if not GenerateEnum(x) then GenerateFeatureCode(GetNodeAttrib(x, 'name')); end; x := x.NextSibling; end; end; procedure Generate(const xml: TDOMNode); var x: TDOMNode; g: DOMString; begin UpdateComments; g := GetNodeAttrib(xml, 'protect', ''); CreateGuard(g); try CurrentName := GetNodeAttrib(xml, 'name'); if not TryStrToInt(String(GetNodeAttrib(xml, 'number', '-1')), CurrentNumber) then CurrentNumber := -1; WriteLn('generate code for feature: ', CurrentName); x := xml.FirstChild; while Assigned(x) do begin if (x.NodeName = 'require') then GenerateFeatureCode(x); x := x.NextSibling; end; finally CloseGuard(g); UpdateComments; CurrentName := ''; end; end; procedure EnableFeature; var x: TDOMNode; begin GenerateFeatureCode('VK_API_VERSION'); GenerateFeatureCode('VK_API_VERSION_MAJOR'); GenerateFeatureCode('VK_API_VERSION_MINOR'); GenerateFeatureCode('VK_API_VERSION_BUGFIX'); x := fDocument.DocumentElement.FirstChild; while Assigned(x) do begin if (x.NodeName = 'feature') and (GetNodeAttrib(x, 'api', '') = aName) then Generate(x); x := x.NextSibling; end; end; procedure EnableExtensions; var x: TDOMNode; g: String; begin x := fDocument.DocumentElement.FindNode('extensions'); if not Assigned(x) then exit; x := x.FirstChild; while Assigned(x) do begin if (x.NodeName = 'extension') and (GetNodeAttrib(x, 'supported', '') = aName) then Generate(x); x := x.NextSibling; end; end; procedure RemoveEmptyLines(const aText: TStrings); var i: Integer; b: Boolean; rx: TRegExpr; begin i := 0; rx := TRegExpr.Create('{%.*?%}'); try while (i < aText.Count) do begin if (rx.Exec(aText[i])) then begin aText.Delete(i); continue; end; if (Trim(aText[i]) = '') then begin if b then begin aText.Delete(i); continue; end else b := true; end else b := false; inc(i); end; finally FreeAndNil(rx); end; end; procedure PrepareTemplate; var sl: TStringList; s, n: String; st: TStringTree; p0, p1: Integer; begin sl := TStringList.Create; try sl.LoadFromFile(aTemplate); s := sl.Text; p0 := Pos('{%', s); while (p0 > 0) do begin p1 := Pos('%}', s, p0); if (p1 <= 0) then raise Exception.Create('unclosed tag in template'); n := Trim(Copy(s, p0+2, p1-p0-2)); st := Code.Items[n]; n := Copy(s, p0, p1 - p0 + 2); if Assigned(st) then s := StringReplace(s, n, st.Text, [rfReplaceAll]) else s := StringReplace(s, n, '', [rfReplaceAll]); p0 := Pos('{%', s, p0); end; sl.Text := Trim(s); RemoveEmptyLines(sl); sl.SaveToFile(aOutput); finally FreeAndNil(sl); end; end; begin if not Assigned(fDocument) then raise Exception.Create('no xml file loaded, did you call ReadFile before?'); Code := TStringTree.Create; PosMap := TPosMap.Create; try Code.GetOrCreateItem(KEYWORD_COMMENT).LinesBefore.Text := Trim(String(fComment)); with Code.GetOrCreateItem(KEYWORD_INTF) do begin GetOrCreateItem(KEYWORD_CONST).LinesAfter.Add(''); GetOrCreateItem(KEYWORD_TYPES).LinesAfter.Add(''); GetOrCreateItem(KEYWORD_ENUM).LinesAfter.Add(''); GetOrCreateItem(KEYWORD_FUNCPTR).LinesAfter.Add(''); GetOrCreateItem(KEYWORD_STRUCT).LinesAfter.Add(''); GetOrCreateItem(KEYWORD_COMMAND).LinesAfter.Add(''); GetOrCreateItem(KEYWORD_VAR).LinesAfter.Add(''); end; with Code.GetOrCreateItem(KEYWORD_IMPL) do begin GetOrCreateItem(KEYWORD_CODE).LinesAfter.Add(''); end; Code.GetOrCreateItem(KEYWORD_IMPL); EnableFeature; EnableExtensions; PrepareTemplate; finally FreeAndNil(PosMap); FreeAndNil(Code); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.ReadFile(const aFilename: String); var fs: TFileStream; begin if not FileExists(aFilename) then raise Exception.Create('file does not exist: ' + aFilename); fs := TFileStream.Create(aFilename, fmOpenRead); try ReadFile(fs); finally FreeAndNil(fs); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.ReadFile(const aStream: TStream); var xml: TDOMNode; procedure ParseTypes(const xml: TDOMNode); var x: TDOMNode; begin x := xml.FirstChild; while Assigned(x) do begin TvkObject.CreateFromXml(x, fObjects); x := x.NextSibling; end; end; begin Clear; ReadXMLFile(fDocument, aStream); WriteLn('read xml data'); xml := fDocument.DocumentElement.FirstChild; while Assigned(xml) do begin if (xml.NodeName = 'types') or (xml.NodeName = 'commands') then ParseTypes(xml) else if (xml.NodeName = 'enums') then TvkObject.CreateFromXml(xml, fObjects) else if (xml.NodeName = 'comment') then fComment := WideString(Trim(String(xml.TextContent))); xml := xml.NextSibling; end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure THeaderGenerator.Clear; procedure CreateBaseType(const aName, aAlias, aParent: DOMString); var t: TvkTypeBase; begin t := TvkTypeBase.Create(otType); try t.Name := aName; t.Alias := aAlias; t.Parent := aParent; t.List := fObjects; fObjects.Add(t); except FreeAndNil(t); raise; end; end; begin fObjects.Clear; CreateBaseType('void', 'VkVoid', ''); CreateBaseType('uint8_t', 'VkUint8', ''); CreateBaseType('uint16_t', 'VkUint16', ''); CreateBaseType('uint32_t', 'VkUint32', ''); CreateBaseType('uint64_t', 'VkUint64', ''); CreateBaseType('int16_t', 'VkInt16', ''); CreateBaseType('int32_t', 'VkInt32', ''); CreateBaseType('int64_t', 'VkInt64', ''); CreateBaseType('float', 'VkFloat', ''); CreateBaseType('double', 'VkDouble', ''); CreateBaseType('char', 'VkChar', ''); CreateBaseType('size_t', 'VkSize', ''); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor THeaderGenerator.Create; begin inherited Create; fObjects := TvkObjectList.Create; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// destructor THeaderGenerator.Destroy; begin FreeAndNil(fObjects); FreeAndNil(fDocument); inherited Destroy; end; end.