|
@@ -1,1088 +1,1092 @@
|
|
|
-{ ***************************************************************************
|
|
|
-
|
|
|
- Copyright (c) 2015-2022 Kike Pérez
|
|
|
-
|
|
|
- Unit : Quick.YAML
|
|
|
- Description : YAML Object parser
|
|
|
- Author : Kike Pérez
|
|
|
- Version : 1.1
|
|
|
- Created : 17/04/2019
|
|
|
- Modified : 07/03/2022
|
|
|
-
|
|
|
- This file is part of QuickLib: https://github.com/exilon/QuickLib
|
|
|
-
|
|
|
- ***************************************************************************
|
|
|
-
|
|
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
- you may not use this file except in compliance with the License.
|
|
|
- You may obtain a copy of the License at
|
|
|
-
|
|
|
- http://www.apache.org/licenses/LICENSE-2.0
|
|
|
-
|
|
|
- Unless required by applicable law or agreed to in writing, software
|
|
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
- See the License for the specific language governing permissions and
|
|
|
- limitations under the License.
|
|
|
-
|
|
|
- *************************************************************************** }
|
|
|
-
|
|
|
-unit Quick.YAML;
|
|
|
-
|
|
|
-{$i QuickLib.inc}
|
|
|
-
|
|
|
-interface
|
|
|
-
|
|
|
-uses
|
|
|
- Classes,
|
|
|
- SysUtils,
|
|
|
- Quick.Commons,
|
|
|
- Generics.Collections,
|
|
|
- Quick.Value;
|
|
|
-
|
|
|
-type
|
|
|
- TYamlScalar = TFlexValue;
|
|
|
-
|
|
|
- TYamlAncestor = class abstract
|
|
|
- protected
|
|
|
- fOwned : Boolean;
|
|
|
- function IsNull : Boolean; virtual;
|
|
|
- procedure AddDescendant(const aDescendent: TYamlAncestor); virtual;
|
|
|
- public
|
|
|
- constructor Create;
|
|
|
- property Owned : Boolean read fOwned write fOwned;
|
|
|
- property Null : Boolean read IsNull;
|
|
|
- function IsScalar : Boolean; virtual;
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlValue = class abstract(TYamlAncestor)
|
|
|
- public
|
|
|
- function Value : TFlexValue; virtual;
|
|
|
- function AsString : string; virtual; abstract;
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlString = class(TYamlValue)
|
|
|
- private
|
|
|
- fValue : string;
|
|
|
- fIsNull : Boolean;
|
|
|
- protected
|
|
|
- function IsNull : Boolean; override;
|
|
|
- public
|
|
|
- constructor Create; overload;
|
|
|
- constructor Create(const aValue : string); overload;
|
|
|
- function Value : TFlexValue; override;
|
|
|
- function IsScalar : Boolean; override;
|
|
|
- function AsString : string; override;
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlInteger = class(TYamlValue)
|
|
|
- private
|
|
|
- fValue : Integer;
|
|
|
- fIsNull : Boolean;
|
|
|
- protected
|
|
|
- function IsNull : Boolean; override;
|
|
|
- public
|
|
|
- constructor Create; overload;
|
|
|
- constructor Create(const aValue : Integer); overload;
|
|
|
- function Value : TFlexValue; override;
|
|
|
- function IsScalar : Boolean; override;
|
|
|
- function AsString : string; override;
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlFloat = class(TYamlValue)
|
|
|
- private
|
|
|
- fValue : Double;
|
|
|
- fIsNull : Boolean;
|
|
|
- protected
|
|
|
- function IsNull : Boolean; override;
|
|
|
- public
|
|
|
- constructor Create; overload;
|
|
|
- constructor Create(const aValue : Double); overload;
|
|
|
- function Value : TFlexValue; override;
|
|
|
- function IsScalar : Boolean; override;
|
|
|
- function AsString : string; override;
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlBoolean = class(TYamlValue)
|
|
|
- private
|
|
|
- fValue : Boolean;
|
|
|
- fIsNull : Boolean;
|
|
|
- protected
|
|
|
- function IsNull : Boolean; override;
|
|
|
- public
|
|
|
- constructor Create; overload;
|
|
|
- constructor Create(const aValue : Boolean); overload;
|
|
|
- function Value : TFlexValue; override;
|
|
|
- function IsScalar : Boolean; override;
|
|
|
- function AsString : string; override;
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlNull = class(TYamlValue)
|
|
|
- protected
|
|
|
- function IsNull: Boolean; override;
|
|
|
- public
|
|
|
- function Value : TFlexValue; override;
|
|
|
- function AsString : string; override;
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlComment = class(TYamlValue)
|
|
|
- private
|
|
|
- fValue : string;
|
|
|
- fIsNull : Boolean;
|
|
|
- protected
|
|
|
- function IsNull : Boolean; override;
|
|
|
- public
|
|
|
- constructor Create; overload;
|
|
|
- constructor Create(const aComment : string); overload;
|
|
|
- function Value : TFlexValue; override;
|
|
|
- function IsScalar : Boolean; override;
|
|
|
- function AsString : string; override;
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlPair = class(TYamlAncestor)
|
|
|
- private
|
|
|
- fName : string;
|
|
|
- fValue : TYamlValue;
|
|
|
- protected
|
|
|
- procedure AddDescendant(const aDescendent: TYamlAncestor); override;
|
|
|
- public
|
|
|
- constructor Create(const aName : string; const aValue : TYamlValue); overload;
|
|
|
- constructor Create(const aName : string; const aValue : string); overload;
|
|
|
- constructor Create(const aName : string; const aValue : Integer); overload;
|
|
|
- constructor Create(const aName : string; const aValue : Double); overload;
|
|
|
- destructor Destroy; override;
|
|
|
- property Name : string read fName write fName;
|
|
|
- property Value : TYamlValue read fValue write fValue;
|
|
|
- function ToYaml : string;
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlWriter = class
|
|
|
- private
|
|
|
- fData : string;
|
|
|
- public
|
|
|
- constructor Create;
|
|
|
- property Text : string read fData;
|
|
|
- procedure Write(const aValue : string);
|
|
|
- procedure Writeln(const aValue : string);
|
|
|
- end;
|
|
|
-
|
|
|
- TYamlObject = class(TYamlValue)
|
|
|
- public type
|
|
|
- TEnumerator = class
|
|
|
- private
|
|
|
- fIndex: Integer;
|
|
|
- fObject: TYamlObject;
|
|
|
- public
|
|
|
- constructor Create(const aObject: TYamlObject);
|
|
|
- function GetCurrent: TYamlPair; inline;
|
|
|
- function MoveNext: Boolean; inline;
|
|
|
- property Current: TYamlPair read GetCurrent;
|
|
|
- end;
|
|
|
- private
|
|
|
- fMembers : TList<TYamlPair>;
|
|
|
- function GetCount : Integer;
|
|
|
- class function ParseValue(yaml : TList<string>; var vIndex : Integer): TYamlAncestor;
|
|
|
- class function ParsePairName(const aPair : string) : string;
|
|
|
- class function ParsePairValue(const aPair : string) : string;
|
|
|
- class function ParseArrayValue(const aValue : string) : TYamlValue;
|
|
|
- class function GetItemLevel(const aValue : string) : Integer;
|
|
|
- function ParseToYaml(aIndent : Integer) : string;
|
|
|
- protected
|
|
|
- procedure AddDescendant(const aDescendent: TYamlAncestor); override;
|
|
|
- public
|
|
|
- constructor Create; overload;
|
|
|
- constructor Create(const aData : string); overload;
|
|
|
- destructor Destroy; override;
|
|
|
- function GetValue(const aName: string): TYamlValue;
|
|
|
- property Values[const aName: string] : TYamlValue read GetValue;
|
|
|
- procedure ParseYaml(const aData : string);
|
|
|
- property Count : Integer read GetCount;
|
|
|
- class function ParseYamlValue(const aData : string) : TYamlAncestor;
|
|
|
- function GetPair(const aIndex : Integer) : TYamlPair;
|
|
|
- function GetPairByName(const aPairName : string) : TYamlPair;
|
|
|
- function AddPair(const aPair : TYamlPair): TYamlObject; overload;
|
|
|
- function AddPair(const aName : string; const aValue : TYamlValue): TYamlObject; overload;
|
|
|
- function AddPair(const aName : string; const aValue : string): TYamlObject; overload;
|
|
|
- function RemovePair(const aPairName: string): TYamlPair;
|
|
|
- function GetEnumerator: TEnumerator; inline;
|
|
|
- property Pairs[const aIndex: Integer]: TYamlPair read GetPair;
|
|
|
- function ToYaml : string;
|
|
|
- function AsString : string; override;
|
|
|
- end;
|
|
|
-
|
|
|
- { TYamlArray }
|
|
|
-
|
|
|
- TYamlArray = class(TYamlValue)
|
|
|
- public type
|
|
|
- TEnumerator = class
|
|
|
- private
|
|
|
- fIndex : Integer;
|
|
|
- fArray : TYamlArray;
|
|
|
- public
|
|
|
- constructor Create(const aArray: TYamlArray);
|
|
|
- function GetCurrent: TYamlValue; inline;
|
|
|
- function MoveNext: Boolean; inline;
|
|
|
- property Current: TYamlValue read GetCurrent;
|
|
|
- end;
|
|
|
- private
|
|
|
- fElements: TList<TYamlValue>;
|
|
|
- function ParseToYaml(aIndent : Integer; var vIsScalar : Boolean) : string;
|
|
|
- protected
|
|
|
- procedure AddDescendant(const aDescendant: TYamlAncestor); override;
|
|
|
- function GetCount: Integer; inline;
|
|
|
- function GetValue(const aIndex: Integer): TYamlValue; overload; inline;
|
|
|
- public
|
|
|
- constructor Create; overload;
|
|
|
- constructor Create(const aFirstElem: TYamlValue); overload;
|
|
|
- destructor Destroy; override;
|
|
|
- property Count: Integer read GetCount;
|
|
|
- property Items[const aIndex: Integer]: TYamlValue read GetValue;
|
|
|
- procedure AddElement(const aElement: TYamlValue);
|
|
|
- function GetEnumerator: TEnumerator; inline;
|
|
|
- function AsString : string; override;
|
|
|
- end;
|
|
|
-
|
|
|
- EYAMLException = class(Exception);
|
|
|
-
|
|
|
-implementation
|
|
|
-
|
|
|
-const
|
|
|
- NUM_INDENT = 2;
|
|
|
-
|
|
|
-{ TYamlAncestor }
|
|
|
-
|
|
|
-procedure TYamlAncestor.AddDescendant(const aDescendent: TYamlAncestor);
|
|
|
-begin
|
|
|
- raise EYAMLException.CreateFmt('Cannot add value %s to %s',[aDescendent.ClassName,ClassName]);
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlAncestor.Create;
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fOwned := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlAncestor.IsNull: Boolean;
|
|
|
-begin
|
|
|
- Result := False;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlAncestor.IsScalar: Boolean;
|
|
|
-begin
|
|
|
- Result := False;
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlObject }
|
|
|
-
|
|
|
-function TYamlObject.AddPair(const aPair: TYamlPair): TYamlObject;
|
|
|
-begin
|
|
|
- if aPair <> nil then AddDescendant(aPair);
|
|
|
- Result := Self;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.AddPair(const aName: string; const aValue: TYamlValue): TYamlObject;
|
|
|
-begin
|
|
|
- if (not aName.IsEmpty) and (aValue <> nil) then AddPair(TYamlPair.Create(aName,aValue));
|
|
|
- Result := Self;
|
|
|
-end;
|
|
|
-
|
|
|
-procedure TYamlObject.AddDescendant(const aDescendent: TYamlAncestor);
|
|
|
-begin
|
|
|
- if aDescendent <> nil then fMembers.Add(TYamlPair(aDescendent));
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.AddPair(const aName, aValue: string): TYamlObject;
|
|
|
-begin
|
|
|
- if not aName.IsEmpty and (not aValue.IsEmpty) then AddPair(TYamlPair.Create(aName,aValue));
|
|
|
- Result := Self;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.AsString: string;
|
|
|
-begin
|
|
|
- Result := ToYaml;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlObject.Create(const aData: string);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- ParseYaml(aData);
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlObject.Create;
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fMembers := TList<TYamlPair>.Create;
|
|
|
-end;
|
|
|
-
|
|
|
-destructor TYamlObject.Destroy;
|
|
|
-var
|
|
|
- member: TYamlAncestor;
|
|
|
- i: Integer;
|
|
|
-begin
|
|
|
- if Assigned(fMembers) then
|
|
|
- for i := 0 to fMembers.Count - 1 do
|
|
|
- begin
|
|
|
- {$IFNDEF FPC}
|
|
|
- member := fMembers.List[i];
|
|
|
- {$ELSE}
|
|
|
- member := fMembers.Items[i];
|
|
|
- {$ENDIF}
|
|
|
- if Assigned(member) and member.Owned then member.Free;
|
|
|
- end;
|
|
|
- FreeAndNil(fMembers);
|
|
|
- inherited;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.GetCount: Integer;
|
|
|
-begin
|
|
|
- Result := fMembers.Count;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.GetEnumerator: TEnumerator;
|
|
|
-begin
|
|
|
- Result := TEnumerator.Create(Self);
|
|
|
-end;
|
|
|
-
|
|
|
-class function TYamlObject.GetItemLevel(const aValue: string): Integer;
|
|
|
-var
|
|
|
- i : Integer;
|
|
|
- trimed : string;
|
|
|
-begin
|
|
|
- trimed := aValue.Trim;
|
|
|
- if trimed.IsEmpty or trimed.StartsWith('#') then Exit(99999);
|
|
|
-
|
|
|
- for i := Low(aValue) to aValue.Length do
|
|
|
- begin
|
|
|
- if aValue[i] <> ' ' then Exit(i);
|
|
|
- end;
|
|
|
- Result := Low(aValue);
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.GetPair(const aIndex: Integer): TYamlPair;
|
|
|
-begin
|
|
|
- Result := fMembers[aIndex];
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.GetPairByName(const aPairName: string): TYamlPair;
|
|
|
-var
|
|
|
- yamlpair : TYamlPair;
|
|
|
- I: Integer;
|
|
|
-begin
|
|
|
- for i := 0 to Count - 1 do
|
|
|
- begin
|
|
|
- {$IFNDEF FPC}
|
|
|
- yamlpair := fMembers.List[i];
|
|
|
- {$ELSE}
|
|
|
- yamlpair := fMembers.Items[i];
|
|
|
- {$ENDIF}
|
|
|
- if CompareText(yamlpair.Name,aPairName) = 0 then Exit(yamlpair);
|
|
|
- end;
|
|
|
- Result := nil;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.GetValue(const aName: string): TYamlValue;
|
|
|
-var
|
|
|
- ymlpair: TYamlPair;
|
|
|
- i: Integer;
|
|
|
-begin
|
|
|
- for i := 0 to Count - 1 do
|
|
|
- begin
|
|
|
- {$IFNDEF FPC}
|
|
|
- ymlpair := fMembers.List[i];
|
|
|
- {$ELSE}
|
|
|
- ymlpair := fMembers.Items[i];
|
|
|
- {$ENDIF}
|
|
|
- if CompareText(ymlpair.Name,aName) = 0 then Exit(ymlpair.Value);
|
|
|
- end;
|
|
|
- Result := nil;
|
|
|
-end;
|
|
|
-
|
|
|
-class function TYamlObject.ParseArrayValue(const aValue: string): TYamlValue;
|
|
|
-var
|
|
|
- nint : Int64;
|
|
|
- nfloat : Double;
|
|
|
-begin
|
|
|
- if TryStrToInt64(aValue,nint) then Result := TYamlInteger.Create(nint)
|
|
|
- else if TryStrToFloat(aValue,nfloat) then Result := TYamlFloat.Create(nfloat)
|
|
|
- else Result := TYamlString.Create(aValue);
|
|
|
-end;
|
|
|
-
|
|
|
-class function TYamlObject.ParsePairName(const aPair: string): string;
|
|
|
-begin
|
|
|
- Result := Copy(aPair,0,aPair.IndexOf(':'));
|
|
|
-end;
|
|
|
-
|
|
|
-class function TYamlObject.ParsePairValue(const aPair: string): string;
|
|
|
-begin
|
|
|
- Result := Copy(aPair,aPair.IndexOf(':')+2,aPair.Length).Trim;
|
|
|
-end;
|
|
|
-
|
|
|
-class function TYamlObject.ParseValue(yaml : TList<string>; var vIndex : Integer): TYamlAncestor;
|
|
|
-type
|
|
|
- TYamlType = (ytObject, ytArray, ytScalarArray, ytScalar);
|
|
|
-var
|
|
|
- name : string;
|
|
|
- value : string;
|
|
|
- yvalue : TYamlAncestor;
|
|
|
- level : Integer;
|
|
|
- nextlevel : Integer;
|
|
|
- aitem : string;
|
|
|
- yamlType : TYamlType;
|
|
|
-begin
|
|
|
- Result := nil;
|
|
|
- level := 0;
|
|
|
- while yaml.Count > vIndex do
|
|
|
- begin
|
|
|
- value := yaml[vIndex].Trim;
|
|
|
-
|
|
|
- name := ParsePairName(value);
|
|
|
- if (name.IsEmpty) or (value.IsEmpty) or (value.StartsWith('#')) or (value.StartsWith(#9)) then Exit(nil)
|
|
|
- //else if value.StartsWith('#') then Exit(TYamlComment.Create(value))
|
|
|
- else if value.StartsWith('-') then
|
|
|
- begin
|
|
|
- yaml[vIndex] := StringReplace(yaml[vIndex],'-','',[]).TrimLeft;
|
|
|
- yamlType := ytObject;
|
|
|
- Dec(vIndex);
|
|
|
- end
|
|
|
- else if value.EndsWith(':') then
|
|
|
- begin
|
|
|
- if yaml[vIndex + 1].TrimLeft.StartsWith('-') then yamlType := ytArray
|
|
|
- else yamlType := ytObject;
|
|
|
- end
|
|
|
- else if value.IndexOf(':') < value.Length then
|
|
|
- begin
|
|
|
- value := ParsePairValue(value);
|
|
|
- if (value.StartsWith('[')) and (value.EndsWith(']')) then yamlType := ytScalarArray
|
|
|
- else yamlType := ytScalar;
|
|
|
- end
|
|
|
- else yamlType := TYamlType.ytScalar;
|
|
|
-
|
|
|
- case yamlType of
|
|
|
- ytArray : //is array
|
|
|
- begin
|
|
|
- yvalue := TYamlArray.Create;
|
|
|
- level := GetItemLevel(yaml[vIndex + 1]);
|
|
|
- repeat
|
|
|
- Inc(vIndex);
|
|
|
- yvalue.AddDescendant(ParseValue(yaml,vIndex));
|
|
|
- until (yvalue = nil) or (vIndex >= yaml.Count - 1) or (GetItemLevel(yaml[vIndex + 1]) < level);
|
|
|
- Exit(TYamlPair.Create(name,TYamlValue(yvalue)));
|
|
|
- end;
|
|
|
- ytObject : //is object
|
|
|
- begin
|
|
|
- yvalue := TYamlObject.Create;
|
|
|
- repeat
|
|
|
- Inc(vIndex);
|
|
|
- nextlevel := GetItemLevel(yaml[vIndex]);
|
|
|
- if nextlevel <> 99999 then level := nextlevel;
|
|
|
-
|
|
|
- yvalue.AddDescendant(ParseValue(yaml,vIndex));
|
|
|
- //level := GetItemLevel(yaml[vIndex]);
|
|
|
- //var level2 := GetItemLevel(yaml[offset + 1]);
|
|
|
- until (yvalue = nil) or (vIndex >= yaml.Count - 1) or (GetItemLevel(yaml[vIndex + 1]) < level);
|
|
|
- Exit(TYamlPair.Create(name,TYamlValue(yvalue)));
|
|
|
- end;
|
|
|
- ytScalarArray : //is scalar array
|
|
|
- begin
|
|
|
- yvalue := TYamlArray.Create;
|
|
|
- value := StringReplace(Copy(value,2,Value.Length-2),', ',#9,[rfReplaceAll]);
|
|
|
- for aitem in value.Split([#9]) do
|
|
|
- begin
|
|
|
- yvalue.AddDescendant(ParseArrayValue(aitem));
|
|
|
- end;
|
|
|
- Exit(TYamlPair.Create(name,TYamlValue(yvalue)));
|
|
|
- end;
|
|
|
- else Exit(TYamlPair.Create(name,value)); //is scalar
|
|
|
- end;
|
|
|
- Inc(vIndex);
|
|
|
- end;
|
|
|
-end;
|
|
|
-
|
|
|
-procedure TYamlObject.ParseYaml(const aData: string);
|
|
|
-var
|
|
|
- yaml : TList<string>;
|
|
|
- line : string;
|
|
|
- data : string;
|
|
|
- yamlvalue : TYamlAncestor;
|
|
|
- vIndex : Integer;
|
|
|
-begin
|
|
|
- yaml := TList<string>.Create;
|
|
|
- try
|
|
|
- vIndex := 0;
|
|
|
- //normalize tabs
|
|
|
- data := StringReplace(aData,#9,Spaces(NUM_INDENT),[rfReplaceAll]);
|
|
|
- {$IFNDEF LINUX}
|
|
|
- for line in data.Split([#13]) do yaml.Add(StringReplace(line,#10,'',[rfReplaceAll]));
|
|
|
- {$ELSE}
|
|
|
- for line in data.Split([#10]) do yaml.Add(StringReplace(line,#13,'',[rfReplaceAll]));
|
|
|
- {$ENDIF}
|
|
|
- while yaml.Count > vIndex do
|
|
|
- begin
|
|
|
- yamlvalue := ParseValue(yaml,vIndex);
|
|
|
- if yamlvalue <> nil then AddDescendant(yamlvalue);
|
|
|
- Inc(vIndex);
|
|
|
- end;
|
|
|
- finally
|
|
|
- yaml.Free;
|
|
|
- end;
|
|
|
-end;
|
|
|
-
|
|
|
-class function TYamlObject.ParseYamlValue(const aData : string) : TYamlAncestor;
|
|
|
-var
|
|
|
- yaml : TList<string>;
|
|
|
- line : string;
|
|
|
- data : string;
|
|
|
- yamlvalue : TYamlAncestor;
|
|
|
- vIndex : Integer;
|
|
|
-begin
|
|
|
- yaml := TList<string>.Create;
|
|
|
- try
|
|
|
- vIndex := 0;
|
|
|
- //normalize tabs
|
|
|
- data := StringReplace(aData,#9,Spaces(NUM_INDENT),[rfReplaceAll]);
|
|
|
- {$IFNDEF LINUX}
|
|
|
- for line in data.Split([#13]) do yaml.Add(StringReplace(line,#10,'',[rfReplaceAll]));
|
|
|
- {$ELSE}
|
|
|
- for line in data.Split([#10]) do yaml.Add(StringReplace(line,#13,'',[rfReplaceAll]));
|
|
|
- {$ENDIF}
|
|
|
- if yaml[0].TrimLeft.StartsWith('- ') then Result := TYamlArray.Create
|
|
|
- else Result := TYamlObject.Create;
|
|
|
- while yaml.Count > vIndex do
|
|
|
- begin
|
|
|
- yamlvalue := ParseValue(yaml,vIndex);
|
|
|
- if yamlvalue <> nil then Result.AddDescendant(yamlvalue);
|
|
|
- Inc(vIndex);
|
|
|
- end;
|
|
|
- finally
|
|
|
- yaml.Free;
|
|
|
- end;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.RemovePair(const aPairName: string): TYamlPair;
|
|
|
-var
|
|
|
- yamlpair: TYamlPair;
|
|
|
- i: Integer;
|
|
|
-begin
|
|
|
- for i := 0 to Count - 1 do
|
|
|
- begin
|
|
|
- {$IFNDEF FPC}
|
|
|
- yamlpair := TYamlPair(FMembers.List[i]);
|
|
|
- {$ELSE}
|
|
|
- yamlpair := TYamlPair(fMembers.Items[i]);
|
|
|
- {$ENDIF}
|
|
|
- if CompareText(yamlpair.Name,aPairName) = 0 then
|
|
|
- begin
|
|
|
- fMembers.Remove(yamlpair);
|
|
|
- Exit(yamlpair);
|
|
|
- end;
|
|
|
- end;
|
|
|
- Result := nil;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.ToYaml: string;
|
|
|
-begin
|
|
|
- Result := ParseToYaml(0);
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.ParseToYaml(aIndent : Integer) : string;
|
|
|
-var
|
|
|
- i : Integer;
|
|
|
- member : TYamlPair;
|
|
|
- yaml : TYamlWriter;
|
|
|
- yvalue : TYamlAncestor;
|
|
|
- indent : string;
|
|
|
- isscalar : Boolean;
|
|
|
- scalar : string;
|
|
|
- rarray : string;
|
|
|
-begin
|
|
|
- yaml := TYamlWriter.Create;
|
|
|
- try
|
|
|
- indent := StringOfChar(' ',aIndent);
|
|
|
- for i := 0 to fMembers.Count - 1 do
|
|
|
- begin
|
|
|
- member := fMembers[i];
|
|
|
- if member = nil then continue;
|
|
|
-
|
|
|
- yvalue := member.Value;
|
|
|
- if (yvalue.IsScalar) or (yvalue is TYamlNull) then
|
|
|
- begin
|
|
|
- if yvalue is TYamlComment then yaml.Writeln(Format('#%s%s',[indent,TYamlComment(member.Value).AsString]))
|
|
|
- else
|
|
|
- begin
|
|
|
- if yvalue is TYamlNull then scalar := 'null'
|
|
|
- else if (yvalue is TYamlFloat) or (yvalue is TYamlBoolean) then scalar := member.Value.AsString
|
|
|
- else scalar := member.Value.Value.AsString;
|
|
|
- if scalar.IsEmpty then scalar := '""';
|
|
|
- yaml.Writeln(Format('%s%s: %s',[indent,member.Name,scalar]));
|
|
|
- if (i < fMembers.Count - 1) and (fMembers[i+1].Value is TYamlComment) then yaml.Writeln('');
|
|
|
- end;
|
|
|
- end
|
|
|
- else if (yvalue is TYamlObject) then
|
|
|
- begin
|
|
|
- yaml.Writeln(Format('%s%s:',[indent,member.Name]));
|
|
|
- yaml.Write((yvalue as TYamlObject).ParseToYaml(aIndent + NUM_INDENT));
|
|
|
- if aIndent = 0 then yaml.Writeln('');
|
|
|
- end
|
|
|
- else if (yvalue is TYamlArray) then
|
|
|
- begin
|
|
|
- isscalar := False;
|
|
|
- rarray := (yvalue as TYamlArray).ParseToYaml(aIndent + NUM_INDENT,isscalar);
|
|
|
- if isscalar then yaml.Writeln(Format('%s%s: %s',[indent,member.Name,rarray]))
|
|
|
- else
|
|
|
- begin
|
|
|
- yaml.Writeln(Format('%s%s:',[indent,member.Name]));
|
|
|
- yaml.Write(rarray);
|
|
|
- end;
|
|
|
- end;
|
|
|
- end;
|
|
|
- Result := yaml.Text;
|
|
|
- finally
|
|
|
- yaml.Free;
|
|
|
- end;
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlString }
|
|
|
-
|
|
|
-constructor TYamlString.Create(const aValue: string);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fValue := aValue;
|
|
|
- fIsNull := False;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlString.Create;
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fIsNull := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlString.IsNull: Boolean;
|
|
|
-begin
|
|
|
- Result := fIsNull;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlString.IsScalar: Boolean;
|
|
|
-begin
|
|
|
- Result := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlString.AsString: string;
|
|
|
-begin
|
|
|
- Result := fValue;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlString.Value: TFlexValue;
|
|
|
-begin
|
|
|
- Result := fValue;
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlInteger }
|
|
|
-
|
|
|
-constructor TYamlInteger.Create(const aValue: Integer);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fValue := aValue;
|
|
|
- fIsNull := False;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlInteger.Create;
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fIsNull := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlInteger.IsNull: Boolean;
|
|
|
-begin
|
|
|
- Result := fIsNull;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlInteger.IsScalar: Boolean;
|
|
|
-begin
|
|
|
- Result := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlInteger.AsString: string;
|
|
|
-begin
|
|
|
- Result := IntToStr(fValue);
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlInteger.Value: TFlexValue;
|
|
|
-begin
|
|
|
- Result := fValue;
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlFloat }
|
|
|
-
|
|
|
-constructor TYamlFloat.Create(const aValue: Double);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fValue := aValue;
|
|
|
- fIsNull := False;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlFloat.Create;
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fIsNull := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlFloat.IsNull: Boolean;
|
|
|
-begin
|
|
|
- Result := fIsNull;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlFloat.IsScalar: Boolean;
|
|
|
-begin
|
|
|
- Result := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlFloat.AsString: string;
|
|
|
-begin
|
|
|
- Result := FloatToStr(fValue);
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlFloat.Value: TFlexValue;
|
|
|
-begin
|
|
|
- Result := fValue;
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlPair }
|
|
|
-
|
|
|
-constructor TYamlPair.Create(const aName: string; const aValue: TYamlValue);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fName := aName;
|
|
|
- fValue := aValue;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlPair.Create(const aName, aValue: string);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fName := aName;
|
|
|
- fValue := TYamlString.Create(aValue);
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlPair.Create(const aName: string; const aValue: Double);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fName := aName;
|
|
|
- fValue := TYamlFloat.Create(aValue);
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlPair.Create(const aName: string; const aValue: Integer);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fName := aName;
|
|
|
- fValue := TYamlInteger.Create(aValue);
|
|
|
-end;
|
|
|
-
|
|
|
-destructor TYamlPair.Destroy;
|
|
|
-begin
|
|
|
- if (fValue <> nil) and fValue.Owned then FreeAndNil(fValue);
|
|
|
- inherited Destroy;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlPair.ToYaml: string;
|
|
|
-var
|
|
|
- isscalar : Boolean;
|
|
|
-begin
|
|
|
- if fValue = nil then Exit('null');
|
|
|
-
|
|
|
- if fValue is TYamlObject then Result := TYamlObject(fValue).ToYaml
|
|
|
- else if fValue is TYamlArray then Result := TYamlArray(fValue).ParseToYaml(0,isscalar)
|
|
|
- else Result := Format('%s: %s',[fName,fValue.Value.AsString]);
|
|
|
-
|
|
|
-end;
|
|
|
-
|
|
|
-procedure TYamlPair.AddDescendant(const aDescendent: TYamlAncestor);
|
|
|
-begin
|
|
|
- if fName = '' then
|
|
|
- fName := TYamlString(aDescendent).Value
|
|
|
- else if fValue = nil then
|
|
|
- fValue:= TYamlValue(aDescendent)
|
|
|
- else inherited AddDescendant(aDescendent);
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlObject.TEnumerator }
|
|
|
-
|
|
|
-constructor TYamlObject.TEnumerator.Create(const aObject: TYamlObject);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fIndex := -1;
|
|
|
- fObject := aObject;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.TEnumerator.GetCurrent: TYamlPair;
|
|
|
-begin
|
|
|
- {$IFNDEF FPC}
|
|
|
- Result := fObject.fMembers.List[fIndex];
|
|
|
- {$ELSE}
|
|
|
- Result := fObject.fMembers.Items[fIndex];
|
|
|
- {$ENDIF}
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlObject.TEnumerator.MoveNext: Boolean;
|
|
|
-begin
|
|
|
- Inc(fIndex);
|
|
|
- Result := fIndex < fObject.Count;
|
|
|
-end;
|
|
|
-
|
|
|
-
|
|
|
-{ TYamlValue }
|
|
|
-
|
|
|
-function TYamlValue.Value: TFlexValue;
|
|
|
-begin
|
|
|
- Result := '';
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlArray.TEnumerator }
|
|
|
-
|
|
|
-constructor TYamlArray.TEnumerator.Create(const aArray: TYamlArray);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fIndex := -1;
|
|
|
- fArray := aArray;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlArray.TEnumerator.GetCurrent: TYamlValue;
|
|
|
-begin
|
|
|
- {$IFNDEF FPC}
|
|
|
- Result := fArray.fElements.List[fIndex];
|
|
|
- {$ELSE}
|
|
|
- Result := fArray.fElements.Items[fIndex];
|
|
|
- {$ENDIF}
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlArray.TEnumerator.MoveNext: Boolean;
|
|
|
-begin
|
|
|
- Inc(fIndex);
|
|
|
- Result := fIndex < fArray.Count;
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlArray }
|
|
|
-
|
|
|
-procedure TYamlArray.AddDescendant(const aDescendant: TYamlAncestor);
|
|
|
-begin
|
|
|
- fElements.Add(TYamlValue(aDescendant));
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlArray.Create;
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fElements := TList<TYamlValue>.Create;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlArray.Create(const aFirstElem: TYamlValue);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- AddElement(aFirstElem);
|
|
|
-end;
|
|
|
-
|
|
|
-procedure TYamlArray.AddElement(const aElement: TYamlValue);
|
|
|
-begin
|
|
|
- if aElement <> nil then AddDescendant(aElement);
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlArray.AsString: string;
|
|
|
-var
|
|
|
- first : Boolean;
|
|
|
- element : TYamlValue;
|
|
|
-begin
|
|
|
- first := True;
|
|
|
- for element in fElements do
|
|
|
- begin
|
|
|
- if first then Result := Result + element.AsString
|
|
|
- else Result := Result + ',' + element.AsString;
|
|
|
- end;
|
|
|
- Result := Format('[%s]',[Result]);
|
|
|
-end;
|
|
|
-
|
|
|
-destructor TYamlArray.Destroy;
|
|
|
-var
|
|
|
- element: TYamlAncestor;
|
|
|
- i: Integer;
|
|
|
-begin
|
|
|
- if Assigned(fElements) then
|
|
|
- for i := 0 to fElements.Count - 1 do
|
|
|
- begin
|
|
|
- element := fElements[i];
|
|
|
- if Assigned(element) and (element.Owned) then element.Free;
|
|
|
- end;
|
|
|
- if Assigned(fElements) then FreeAndNil(fElements);
|
|
|
- inherited Destroy;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlArray.GetCount: Integer;
|
|
|
-begin
|
|
|
- Result := fElements.Count;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlArray.GetEnumerator: TEnumerator;
|
|
|
-begin
|
|
|
- Result := TEnumerator.Create(Self);
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlArray.GetValue(const aIndex: Integer): TYamlValue;
|
|
|
-begin
|
|
|
- Result := fElements[aIndex];
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlArray.ParseToYaml(aIndent : Integer; var vIsScalar : Boolean) : string;
|
|
|
-var
|
|
|
- element : TYamlValue;
|
|
|
- yaml : TYamlWriter;
|
|
|
- yvalue : TYamlAncestor;
|
|
|
- indent : string;
|
|
|
- isscalar : Boolean;
|
|
|
-begin
|
|
|
- Result := '';
|
|
|
- yvalue := nil;
|
|
|
- yaml := TYamlWriter.Create;
|
|
|
- try
|
|
|
- indent := StringOfChar(' ',aIndent);
|
|
|
- if fElements.Count = 0 then
|
|
|
- begin
|
|
|
- vIsScalar := True;
|
|
|
- Exit('[]');
|
|
|
- end;
|
|
|
- for element in fElements do
|
|
|
- begin
|
|
|
- yvalue := element;
|
|
|
- if yvalue is TYamlPair then yvalue := TYamlPair(yvalue).value;
|
|
|
-
|
|
|
- if yvalue.IsScalar then
|
|
|
- begin
|
|
|
- {$IFNDEF FPC}
|
|
|
- if Result = '' then Result := element.AsString
|
|
|
- else Result := Result + ', ' + element.AsString;
|
|
|
- {$ELSE}
|
|
|
- if Result = '' then Result := TYamlPair(element).Value.AsString
|
|
|
- else Result := Result + ', ' + TYamlPair(element).Value.AsString;
|
|
|
- {$ENDIF}
|
|
|
- end
|
|
|
- else if (yvalue is TYamlObject) then
|
|
|
- begin
|
|
|
- yaml.Write(indent + '- ' + (yvalue as TYamlObject).ParseToYaml(aIndent + NUM_INDENT).TrimLeft);
|
|
|
- end
|
|
|
- else if (yvalue is TYamlArray) then
|
|
|
- begin
|
|
|
- yaml.Write(Format('%s%s',[indent,(yvalue as TYamlArray).ParseToYaml(aIndent + NUM_INDENT,isscalar)]))
|
|
|
- end;
|
|
|
- yaml.Writeln('');
|
|
|
- end;
|
|
|
- if (yvalue <> nil) and (yvalue.IsScalar) then
|
|
|
- begin
|
|
|
- Result := '[' + Result + ']';
|
|
|
- vIsScalar := True;
|
|
|
- end
|
|
|
- else Result := yaml.Text;
|
|
|
- finally
|
|
|
- yaml.Free;
|
|
|
- end;
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlWriter }
|
|
|
-
|
|
|
-procedure TYamlWriter.Write(const aValue: string);
|
|
|
-begin
|
|
|
- fData := fData + aValue;
|
|
|
-end;
|
|
|
-
|
|
|
-procedure TYamlWriter.Writeln(const aValue: string);
|
|
|
-begin
|
|
|
- fData := fData + aValue + CRLF;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlWriter.Create;
|
|
|
-begin
|
|
|
- fData := '';
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlNull }
|
|
|
-
|
|
|
-function TYamlNull.IsNull: Boolean;
|
|
|
-begin
|
|
|
- Result := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlNull.AsString: string;
|
|
|
-begin
|
|
|
- Result := 'null';
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlNull.Value: TFlexValue;
|
|
|
-begin
|
|
|
- Result := nil;
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlBoolean }
|
|
|
-
|
|
|
-constructor TYamlBoolean.Create;
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fIsNull := True;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlBoolean.Create(const aValue: Boolean);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fIsNull := False;
|
|
|
- fValue := aValue;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlBoolean.IsNull: Boolean;
|
|
|
-begin
|
|
|
- Result := fIsNull;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlBoolean.IsScalar: Boolean;
|
|
|
-begin
|
|
|
- Result := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlBoolean.AsString: string;
|
|
|
-begin
|
|
|
- Result := BoolToStr(fValue,True).ToLower;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlBoolean.Value: TFlexValue;
|
|
|
-begin
|
|
|
- Result := fValue;
|
|
|
-end;
|
|
|
-
|
|
|
-{ TYamlComment }
|
|
|
-
|
|
|
-function TYamlComment.AsString: string;
|
|
|
-begin
|
|
|
- Result := fValue;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlComment.Create;
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fIsNull := True;
|
|
|
-end;
|
|
|
-
|
|
|
-constructor TYamlComment.Create(const aComment: string);
|
|
|
-begin
|
|
|
- inherited Create;
|
|
|
- fIsNull := False;
|
|
|
- fValue := aComment;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlComment.IsNull: Boolean;
|
|
|
-begin
|
|
|
- Result := fIsNull;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlComment.IsScalar: Boolean;
|
|
|
-begin
|
|
|
- Result := True;
|
|
|
-end;
|
|
|
-
|
|
|
-function TYamlComment.Value: TFlexValue;
|
|
|
-begin
|
|
|
-
|
|
|
-end;
|
|
|
-
|
|
|
-end.
|
|
|
+{ ***************************************************************************
|
|
|
+
|
|
|
+ Copyright (c) 2015-2022 Kike P�rez
|
|
|
+
|
|
|
+ Unit : Quick.YAML
|
|
|
+ Description : YAML Object parser
|
|
|
+ Author : Kike P�rez
|
|
|
+ Version : 1.1
|
|
|
+ Created : 17/04/2019
|
|
|
+ Modified : 07/03/2022
|
|
|
+
|
|
|
+ This file is part of QuickLib: https://github.com/exilon/QuickLib
|
|
|
+
|
|
|
+ ***************************************************************************
|
|
|
+
|
|
|
+ Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+ you may not use this file except in compliance with the License.
|
|
|
+ You may obtain a copy of the License at
|
|
|
+
|
|
|
+ http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+
|
|
|
+ Unless required by applicable law or agreed to in writing, software
|
|
|
+ distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+ See the License for the specific language governing permissions and
|
|
|
+ limitations under the License.
|
|
|
+
|
|
|
+ *************************************************************************** }
|
|
|
+
|
|
|
+unit Quick.YAML;
|
|
|
+
|
|
|
+{$i QuickLib.inc}
|
|
|
+
|
|
|
+interface
|
|
|
+
|
|
|
+uses
|
|
|
+ Classes,
|
|
|
+ SysUtils,
|
|
|
+ Quick.Commons,
|
|
|
+ Generics.Collections,
|
|
|
+ Quick.Value;
|
|
|
+
|
|
|
+type
|
|
|
+ TYamlScalar = TFlexValue;
|
|
|
+
|
|
|
+ TYamlAncestor = class abstract
|
|
|
+ protected
|
|
|
+ fOwned : Boolean;
|
|
|
+ function IsNull : Boolean; virtual;
|
|
|
+ procedure AddDescendant(const aDescendent: TYamlAncestor); virtual;
|
|
|
+ public
|
|
|
+ constructor Create;
|
|
|
+ property Owned : Boolean read fOwned write fOwned;
|
|
|
+ property Null : Boolean read IsNull;
|
|
|
+ function IsScalar : Boolean; virtual;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlValue = class abstract(TYamlAncestor)
|
|
|
+ public
|
|
|
+ function Value : TFlexValue; virtual;
|
|
|
+ function AsString : string; virtual; abstract;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlString = class(TYamlValue)
|
|
|
+ private
|
|
|
+ fValue : string;
|
|
|
+ fIsNull : Boolean;
|
|
|
+ protected
|
|
|
+ function IsNull : Boolean; override;
|
|
|
+ public
|
|
|
+ constructor Create; overload;
|
|
|
+ constructor Create(const aValue : string); overload;
|
|
|
+ function Value : TFlexValue; override;
|
|
|
+ function IsScalar : Boolean; override;
|
|
|
+ function AsString : string; override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlInteger = class(TYamlValue)
|
|
|
+ private
|
|
|
+ fValue : Integer;
|
|
|
+ fIsNull : Boolean;
|
|
|
+ protected
|
|
|
+ function IsNull : Boolean; override;
|
|
|
+ public
|
|
|
+ constructor Create; overload;
|
|
|
+ constructor Create(const aValue : Integer); overload;
|
|
|
+ function Value : TFlexValue; override;
|
|
|
+ function IsScalar : Boolean; override;
|
|
|
+ function AsString : string; override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlFloat = class(TYamlValue)
|
|
|
+ private
|
|
|
+ fValue : Double;
|
|
|
+ fIsNull : Boolean;
|
|
|
+ protected
|
|
|
+ function IsNull : Boolean; override;
|
|
|
+ public
|
|
|
+ constructor Create; overload;
|
|
|
+ constructor Create(const aValue : Double); overload;
|
|
|
+ function Value : TFlexValue; override;
|
|
|
+ function IsScalar : Boolean; override;
|
|
|
+ function AsString : string; override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlBoolean = class(TYamlValue)
|
|
|
+ private
|
|
|
+ fValue : Boolean;
|
|
|
+ fIsNull : Boolean;
|
|
|
+ protected
|
|
|
+ function IsNull : Boolean; override;
|
|
|
+ public
|
|
|
+ constructor Create; overload;
|
|
|
+ constructor Create(const aValue : Boolean); overload;
|
|
|
+ function Value : TFlexValue; override;
|
|
|
+ function IsScalar : Boolean; override;
|
|
|
+ function AsString : string; override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlNull = class(TYamlValue)
|
|
|
+ protected
|
|
|
+ function IsNull: Boolean; override;
|
|
|
+ public
|
|
|
+ function Value : TFlexValue; override;
|
|
|
+ function AsString : string; override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlComment = class(TYamlValue)
|
|
|
+ private
|
|
|
+ fValue : string;
|
|
|
+ fIsNull : Boolean;
|
|
|
+ protected
|
|
|
+ function IsNull : Boolean; override;
|
|
|
+ public
|
|
|
+ constructor Create; overload;
|
|
|
+ constructor Create(const aComment : string); overload;
|
|
|
+ function Value : TFlexValue; override;
|
|
|
+ function IsScalar : Boolean; override;
|
|
|
+ function AsString : string; override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlPair = class(TYamlAncestor)
|
|
|
+ private
|
|
|
+ fName : string;
|
|
|
+ fValue : TYamlValue;
|
|
|
+ protected
|
|
|
+ procedure AddDescendant(const aDescendent: TYamlAncestor); override;
|
|
|
+ public
|
|
|
+ constructor Create(const aName : string; const aValue : TYamlValue); overload;
|
|
|
+ constructor Create(const aName : string; const aValue : string); overload;
|
|
|
+ constructor Create(const aName : string; const aValue : Integer); overload;
|
|
|
+ constructor Create(const aName : string; const aValue : Double); overload;
|
|
|
+ destructor Destroy; override;
|
|
|
+ property Name : string read fName write fName;
|
|
|
+ property Value : TYamlValue read fValue write fValue;
|
|
|
+ function ToYaml : string;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlWriter = class
|
|
|
+ private
|
|
|
+ fData : string;
|
|
|
+ public
|
|
|
+ constructor Create;
|
|
|
+ property Text : string read fData;
|
|
|
+ procedure Write(const aValue : string);
|
|
|
+ procedure Writeln(const aValue : string);
|
|
|
+ end;
|
|
|
+
|
|
|
+ TYamlObject = class(TYamlValue)
|
|
|
+ public type
|
|
|
+ TEnumerator = class
|
|
|
+ private
|
|
|
+ fIndex: Integer;
|
|
|
+ fObject: TYamlObject;
|
|
|
+ public
|
|
|
+ constructor Create(const aObject: TYamlObject);
|
|
|
+ function GetCurrent: TYamlPair; inline;
|
|
|
+ function MoveNext: Boolean; inline;
|
|
|
+ property Current: TYamlPair read GetCurrent;
|
|
|
+ end;
|
|
|
+ private
|
|
|
+ fMembers : TList<TYamlPair>;
|
|
|
+ function GetCount : Integer;
|
|
|
+ class function ParseValue(yaml : TList<string>; var vIndex : Integer): TYamlAncestor;
|
|
|
+ class function ParsePairName(const aPair : string) : string;
|
|
|
+ class function ParsePairValue(const aPair : string) : string;
|
|
|
+ class function ParseArrayValue(const aValue : string) : TYamlValue;
|
|
|
+ class function GetItemLevel(const aValue : string) : Integer;
|
|
|
+ function ParseToYaml(aIndent : Integer) : string;
|
|
|
+ protected
|
|
|
+ procedure AddDescendant(const aDescendent: TYamlAncestor); override;
|
|
|
+ public
|
|
|
+ constructor Create; overload;
|
|
|
+ constructor Create(const aData : string); overload;
|
|
|
+ destructor Destroy; override;
|
|
|
+ function GetValue(const aName: string): TYamlValue;
|
|
|
+ property Values[const aName: string] : TYamlValue read GetValue;
|
|
|
+ procedure ParseYaml(const aData : string);
|
|
|
+ property Count : Integer read GetCount;
|
|
|
+ class function ParseYamlValue(const aData : string) : TYamlAncestor;
|
|
|
+ function GetPair(const aIndex : Integer) : TYamlPair;
|
|
|
+ function GetPairByName(const aPairName : string) : TYamlPair;
|
|
|
+ function AddPair(const aPair : TYamlPair): TYamlObject; overload;
|
|
|
+ function AddPair(const aName : string; const aValue : TYamlValue): TYamlObject; overload;
|
|
|
+ function AddPair(const aName : string; const aValue : string): TYamlObject; overload;
|
|
|
+ function RemovePair(const aPairName: string): TYamlPair;
|
|
|
+ function GetEnumerator: TEnumerator; inline;
|
|
|
+ property Pairs[const aIndex: Integer]: TYamlPair read GetPair;
|
|
|
+ function ToYaml : string;
|
|
|
+ function AsString : string; override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ { TYamlArray }
|
|
|
+
|
|
|
+ TYamlArray = class(TYamlValue)
|
|
|
+ public type
|
|
|
+ TEnumerator = class
|
|
|
+ private
|
|
|
+ fIndex : Integer;
|
|
|
+ fArray : TYamlArray;
|
|
|
+ public
|
|
|
+ constructor Create(const aArray: TYamlArray);
|
|
|
+ function GetCurrent: TYamlValue; inline;
|
|
|
+ function MoveNext: Boolean; inline;
|
|
|
+ property Current: TYamlValue read GetCurrent;
|
|
|
+ end;
|
|
|
+ private
|
|
|
+ fElements: TList<TYamlValue>;
|
|
|
+ function ParseToYaml(aIndent : Integer; var vIsScalar : Boolean) : string;
|
|
|
+ protected
|
|
|
+ procedure AddDescendant(const aDescendant: TYamlAncestor); override;
|
|
|
+ function GetCount: Integer; inline;
|
|
|
+ function GetValue(const aIndex: Integer): TYamlValue; overload; inline;
|
|
|
+ public
|
|
|
+ constructor Create; overload;
|
|
|
+ constructor Create(const aFirstElem: TYamlValue); overload;
|
|
|
+ destructor Destroy; override;
|
|
|
+ property Count: Integer read GetCount;
|
|
|
+ property Items[const aIndex: Integer]: TYamlValue read GetValue;
|
|
|
+ procedure AddElement(const aElement: TYamlValue);
|
|
|
+ function GetEnumerator: TEnumerator; inline;
|
|
|
+ function AsString : string; override;
|
|
|
+ end;
|
|
|
+
|
|
|
+ EYAMLException = class(Exception);
|
|
|
+
|
|
|
+implementation
|
|
|
+
|
|
|
+const
|
|
|
+ NUM_INDENT = 2;
|
|
|
+
|
|
|
+{ TYamlAncestor }
|
|
|
+
|
|
|
+procedure TYamlAncestor.AddDescendant(const aDescendent: TYamlAncestor);
|
|
|
+begin
|
|
|
+ raise EYAMLException.CreateFmt('Cannot add value %s to %s',[aDescendent.ClassName,ClassName]);
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlAncestor.Create;
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fOwned := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlAncestor.IsNull: Boolean;
|
|
|
+begin
|
|
|
+ Result := False;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlAncestor.IsScalar: Boolean;
|
|
|
+begin
|
|
|
+ Result := False;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlObject }
|
|
|
+
|
|
|
+function TYamlObject.AddPair(const aPair: TYamlPair): TYamlObject;
|
|
|
+begin
|
|
|
+ if aPair <> nil then AddDescendant(aPair);
|
|
|
+ Result := Self;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.AddPair(const aName: string; const aValue: TYamlValue): TYamlObject;
|
|
|
+begin
|
|
|
+ if (not aName.IsEmpty) and (aValue <> nil) then AddPair(TYamlPair.Create(aName,aValue));
|
|
|
+ Result := Self;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TYamlObject.AddDescendant(const aDescendent: TYamlAncestor);
|
|
|
+begin
|
|
|
+ if aDescendent <> nil then fMembers.Add(TYamlPair(aDescendent));
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.AddPair(const aName, aValue: string): TYamlObject;
|
|
|
+begin
|
|
|
+ if not aName.IsEmpty and (not aValue.IsEmpty) then AddPair(TYamlPair.Create(aName,aValue));
|
|
|
+ Result := Self;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.AsString: string;
|
|
|
+begin
|
|
|
+ Result := ToYaml;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlObject.Create(const aData: string);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ ParseYaml(aData);
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlObject.Create;
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fMembers := TList<TYamlPair>.Create;
|
|
|
+end;
|
|
|
+
|
|
|
+destructor TYamlObject.Destroy;
|
|
|
+var
|
|
|
+ member: TYamlAncestor;
|
|
|
+ i: Integer;
|
|
|
+begin
|
|
|
+ if Assigned(fMembers) then
|
|
|
+ for i := 0 to fMembers.Count - 1 do
|
|
|
+ begin
|
|
|
+ {$IFNDEF FPC}
|
|
|
+ member := fMembers.List[i];
|
|
|
+ {$ELSE}
|
|
|
+ member := fMembers.Items[i];
|
|
|
+ {$ENDIF}
|
|
|
+ if Assigned(member) and member.Owned then member.Free;
|
|
|
+ end;
|
|
|
+ FreeAndNil(fMembers);
|
|
|
+ inherited;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.GetCount: Integer;
|
|
|
+begin
|
|
|
+ Result := fMembers.Count;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.GetEnumerator: TEnumerator;
|
|
|
+begin
|
|
|
+ Result := TEnumerator.Create(Self);
|
|
|
+end;
|
|
|
+
|
|
|
+class function TYamlObject.GetItemLevel(const aValue: string): Integer;
|
|
|
+var
|
|
|
+ i : Integer;
|
|
|
+ trimed : string;
|
|
|
+begin
|
|
|
+ trimed := aValue.Trim;
|
|
|
+ if trimed.IsEmpty or trimed.StartsWith('#') then Exit(99999);
|
|
|
+
|
|
|
+ for i := Low(aValue) to aValue.Length do
|
|
|
+ begin
|
|
|
+ if aValue[i] <> ' ' then Exit(i);
|
|
|
+ end;
|
|
|
+ Result := Low(aValue);
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.GetPair(const aIndex: Integer): TYamlPair;
|
|
|
+begin
|
|
|
+ Result := fMembers[aIndex];
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.GetPairByName(const aPairName: string): TYamlPair;
|
|
|
+var
|
|
|
+ yamlpair : TYamlPair;
|
|
|
+ I: Integer;
|
|
|
+begin
|
|
|
+ for i := 0 to Count - 1 do
|
|
|
+ begin
|
|
|
+ {$IFNDEF FPC}
|
|
|
+ yamlpair := fMembers.List[i];
|
|
|
+ {$ELSE}
|
|
|
+ yamlpair := fMembers.Items[i];
|
|
|
+ {$ENDIF}
|
|
|
+ if CompareText(yamlpair.Name,aPairName) = 0 then Exit(yamlpair);
|
|
|
+ end;
|
|
|
+ Result := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.GetValue(const aName: string): TYamlValue;
|
|
|
+var
|
|
|
+ ymlpair: TYamlPair;
|
|
|
+ i: Integer;
|
|
|
+begin
|
|
|
+ for i := 0 to Count - 1 do
|
|
|
+ begin
|
|
|
+ {$IFNDEF FPC}
|
|
|
+ ymlpair := fMembers.List[i];
|
|
|
+ {$ELSE}
|
|
|
+ ymlpair := fMembers.Items[i];
|
|
|
+ {$ENDIF}
|
|
|
+ if CompareText(ymlpair.Name,aName) = 0 then Exit(ymlpair.Value);
|
|
|
+ end;
|
|
|
+ Result := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+class function TYamlObject.ParseArrayValue(const aValue: string): TYamlValue;
|
|
|
+var
|
|
|
+ nint : Int64;
|
|
|
+ nfloat : Double;
|
|
|
+begin
|
|
|
+ if TryStrToInt64(aValue,nint) then Result := TYamlInteger.Create(nint)
|
|
|
+ else if TryStrToFloat(aValue,nfloat) then Result := TYamlFloat.Create(nfloat)
|
|
|
+ else Result := TYamlString.Create(aValue);
|
|
|
+end;
|
|
|
+
|
|
|
+class function TYamlObject.ParsePairName(const aPair: string): string;
|
|
|
+begin
|
|
|
+ Result := Copy(aPair,0,aPair.IndexOf(':'));
|
|
|
+end;
|
|
|
+
|
|
|
+class function TYamlObject.ParsePairValue(const aPair: string): string;
|
|
|
+begin
|
|
|
+ Result := AnsiDequotedStr(Copy(aPair,aPair.IndexOf(':')+2,aPair.Length).Trim, '"');
|
|
|
+end;
|
|
|
+
|
|
|
+class function TYamlObject.ParseValue(yaml : TList<string>; var vIndex : Integer): TYamlAncestor;
|
|
|
+type
|
|
|
+ TYamlType = (ytObject, ytArray, ytScalarArray, ytScalar);
|
|
|
+var
|
|
|
+ name : string;
|
|
|
+ value : string;
|
|
|
+ yvalue : TYamlAncestor;
|
|
|
+ level : Integer;
|
|
|
+ nextlevel : Integer;
|
|
|
+ aitem : string;
|
|
|
+ yamlType : TYamlType;
|
|
|
+begin
|
|
|
+ Result := nil;
|
|
|
+ level := 0;
|
|
|
+ while yaml.Count > vIndex do
|
|
|
+ begin
|
|
|
+ value := yaml[vIndex].Trim;
|
|
|
+
|
|
|
+ name := ParsePairName(value);
|
|
|
+ if (name.IsEmpty) or (value.IsEmpty) or (value.StartsWith('#')) or (value.StartsWith(#9)) then Exit(nil)
|
|
|
+ //else if value.StartsWith('#') then Exit(TYamlComment.Create(value))
|
|
|
+ else if value.StartsWith('-') then
|
|
|
+ begin
|
|
|
+ yaml[vIndex] := StringReplace(yaml[vIndex],'-','',[]).TrimLeft;
|
|
|
+ yamlType := ytObject;
|
|
|
+ Dec(vIndex);
|
|
|
+ end
|
|
|
+ else if value.EndsWith(':') then
|
|
|
+ begin
|
|
|
+ if yaml[vIndex + 1].TrimLeft.StartsWith('-') then yamlType := ytArray
|
|
|
+ else yamlType := ytObject;
|
|
|
+ end
|
|
|
+ else if value.IndexOf(':') < value.Length then
|
|
|
+ begin
|
|
|
+ value := ParsePairValue(value);
|
|
|
+ if (value.StartsWith('[')) and (value.EndsWith(']')) then yamlType := ytScalarArray
|
|
|
+ else yamlType := ytScalar;
|
|
|
+ end
|
|
|
+ else yamlType := TYamlType.ytScalar;
|
|
|
+
|
|
|
+ case yamlType of
|
|
|
+ ytArray : //is array
|
|
|
+ begin
|
|
|
+ yvalue := TYamlArray.Create;
|
|
|
+ level := GetItemLevel(yaml[vIndex + 1]);
|
|
|
+ repeat
|
|
|
+ Inc(vIndex);
|
|
|
+ yvalue.AddDescendant(ParseValue(yaml,vIndex));
|
|
|
+ until (yvalue = nil) or (vIndex >= yaml.Count - 1) or (GetItemLevel(yaml[vIndex + 1]) < level);
|
|
|
+ Exit(TYamlPair.Create(name,TYamlValue(yvalue)));
|
|
|
+ end;
|
|
|
+ ytObject : //is object
|
|
|
+ begin
|
|
|
+ yvalue := TYamlObject.Create;
|
|
|
+ repeat
|
|
|
+ Inc(vIndex);
|
|
|
+ nextlevel := GetItemLevel(yaml[vIndex]);
|
|
|
+ if nextlevel <> 99999 then level := nextlevel;
|
|
|
+
|
|
|
+ yvalue.AddDescendant(ParseValue(yaml,vIndex));
|
|
|
+ //level := GetItemLevel(yaml[vIndex]);
|
|
|
+ //var level2 := GetItemLevel(yaml[offset + 1]);
|
|
|
+ until (yvalue = nil) or (vIndex >= yaml.Count - 1) or (GetItemLevel(yaml[vIndex + 1]) < level);
|
|
|
+ Exit(TYamlPair.Create(name,TYamlValue(yvalue)));
|
|
|
+ end;
|
|
|
+ ytScalarArray : //is scalar array
|
|
|
+ begin
|
|
|
+ yvalue := TYamlArray.Create;
|
|
|
+ value := StringReplace(Copy(value,2,Value.Length-2),', ',#9,[rfReplaceAll]);
|
|
|
+ for aitem in value.Split([#9]) do
|
|
|
+ begin
|
|
|
+ yvalue.AddDescendant(ParseArrayValue(aitem));
|
|
|
+ end;
|
|
|
+ Exit(TYamlPair.Create(name,TYamlValue(yvalue)));
|
|
|
+ end;
|
|
|
+ else Exit(TYamlPair.Create(name,value)); //is scalar
|
|
|
+ end;
|
|
|
+ Inc(vIndex);
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TYamlObject.ParseYaml(const aData: string);
|
|
|
+var
|
|
|
+ yaml : TList<string>;
|
|
|
+ line : string;
|
|
|
+ data : string;
|
|
|
+ yamlvalue : TYamlAncestor;
|
|
|
+ vIndex : Integer;
|
|
|
+begin
|
|
|
+ yaml := TList<string>.Create;
|
|
|
+ try
|
|
|
+ vIndex := 0;
|
|
|
+ //normalize tabs
|
|
|
+ data := StringReplace(aData,#9,Spaces(NUM_INDENT),[rfReplaceAll]);
|
|
|
+ {$IFDEF MSWINDOWS}
|
|
|
+ for line in data.Split([#13]) do yaml.Add(StringReplace(line,#10,'',[rfReplaceAll]));
|
|
|
+ {$ELSE}
|
|
|
+ for line in data.Split([#10]) do yaml.Add(StringReplace(line,#13,'',[rfReplaceAll]));
|
|
|
+ {$ENDIF}
|
|
|
+ while yaml.Count > vIndex do
|
|
|
+ begin
|
|
|
+ yamlvalue := ParseValue(yaml,vIndex);
|
|
|
+ if yamlvalue <> nil then AddDescendant(yamlvalue);
|
|
|
+ Inc(vIndex);
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ yaml.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+class function TYamlObject.ParseYamlValue(const aData : string) : TYamlAncestor;
|
|
|
+var
|
|
|
+ yaml : TList<string>;
|
|
|
+ line : string;
|
|
|
+ data : string;
|
|
|
+ yamlvalue : TYamlAncestor;
|
|
|
+ vIndex : Integer;
|
|
|
+begin
|
|
|
+ yaml := TList<string>.Create;
|
|
|
+ try
|
|
|
+ vIndex := 0;
|
|
|
+ //normalize tabs
|
|
|
+ data := StringReplace(aData,#9,Spaces(NUM_INDENT),[rfReplaceAll]);
|
|
|
+ {$IFDEF MSWINDOWS}
|
|
|
+ for line in data.Split([#13]) do yaml.Add(StringReplace(line,#10,'',[rfReplaceAll]));
|
|
|
+ {$ELSE}
|
|
|
+ for line in data.Split([#10]) do yaml.Add(StringReplace(line,#13,'',[rfReplaceAll]));
|
|
|
+ {$ENDIF}
|
|
|
+ if yaml[0].TrimLeft.StartsWith('- ') then Result := TYamlArray.Create
|
|
|
+ else Result := TYamlObject.Create;
|
|
|
+ while yaml.Count > vIndex do
|
|
|
+ begin
|
|
|
+ yamlvalue := ParseValue(yaml,vIndex);
|
|
|
+ if yamlvalue <> nil then Result.AddDescendant(yamlvalue);
|
|
|
+ Inc(vIndex);
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ yaml.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.RemovePair(const aPairName: string): TYamlPair;
|
|
|
+var
|
|
|
+ yamlpair: TYamlPair;
|
|
|
+ i: Integer;
|
|
|
+begin
|
|
|
+ for i := 0 to Count - 1 do
|
|
|
+ begin
|
|
|
+ {$IFNDEF FPC}
|
|
|
+ yamlpair := TYamlPair(FMembers.List[i]);
|
|
|
+ {$ELSE}
|
|
|
+ yamlpair := TYamlPair(fMembers.Items[i]);
|
|
|
+ {$ENDIF}
|
|
|
+ if CompareText(yamlpair.Name,aPairName) = 0 then
|
|
|
+ begin
|
|
|
+ fMembers.Remove(yamlpair);
|
|
|
+ Exit(yamlpair);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ Result := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.ToYaml: string;
|
|
|
+begin
|
|
|
+ Result := ParseToYaml(0);
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.ParseToYaml(aIndent : Integer) : string;
|
|
|
+const
|
|
|
+ SPECIAL_CHARS: array[1..19] of Char = (':', '{', '}', '[', ']', ',', '&', '*', '#', '?', '|', '-', '<', '>', '=', '!', '%', '@', '\');
|
|
|
+var
|
|
|
+ i : Integer;
|
|
|
+ member : TYamlPair;
|
|
|
+ yaml : TYamlWriter;
|
|
|
+ yvalue : TYamlAncestor;
|
|
|
+ indent : string;
|
|
|
+ isscalar : Boolean;
|
|
|
+ scalar : string;
|
|
|
+ rarray : string;
|
|
|
+begin
|
|
|
+ yaml := TYamlWriter.Create;
|
|
|
+ try
|
|
|
+ indent := StringOfChar(' ',aIndent);
|
|
|
+ for i := 0 to fMembers.Count - 1 do
|
|
|
+ begin
|
|
|
+ member := fMembers[i];
|
|
|
+ if member = nil then continue;
|
|
|
+
|
|
|
+ yvalue := member.Value;
|
|
|
+ if (yvalue.IsScalar) or (yvalue is TYamlNull) then
|
|
|
+ begin
|
|
|
+ if yvalue is TYamlComment then yaml.Writeln(Format('#%s%s',[indent,TYamlComment(member.Value).AsString]))
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ if yvalue is TYamlNull then scalar := 'null'
|
|
|
+ else if (yvalue is TYamlFloat) or (yvalue is TYamlBoolean) then scalar := member.Value.AsString
|
|
|
+ else scalar := member.Value.Value.AsString;
|
|
|
+ if scalar.IsEmpty then scalar := '""';
|
|
|
+ if scalar.IndexOfAny(SPECIAL_CHARS) > -1 then
|
|
|
+ scalar := AnsiQuotedStr(scalar, '"');
|
|
|
+ yaml.Writeln(Format('%s%s: %s',[indent,member.Name,scalar]));
|
|
|
+ if (i < fMembers.Count - 1) and (fMembers[i+1].Value is TYamlComment) then yaml.Writeln('');
|
|
|
+ end;
|
|
|
+ end
|
|
|
+ else if (yvalue is TYamlObject) then
|
|
|
+ begin
|
|
|
+ yaml.Writeln(Format('%s%s:',[indent,member.Name]));
|
|
|
+ yaml.Write((yvalue as TYamlObject).ParseToYaml(aIndent + NUM_INDENT));
|
|
|
+ if aIndent = 0 then yaml.Writeln('');
|
|
|
+ end
|
|
|
+ else if (yvalue is TYamlArray) then
|
|
|
+ begin
|
|
|
+ isscalar := False;
|
|
|
+ rarray := (yvalue as TYamlArray).ParseToYaml(aIndent + NUM_INDENT,isscalar);
|
|
|
+ if isscalar then yaml.Writeln(Format('%s%s: %s',[indent,member.Name,rarray]))
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ yaml.Writeln(Format('%s%s:',[indent,member.Name]));
|
|
|
+ yaml.Write(rarray);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ Result := yaml.Text;
|
|
|
+ finally
|
|
|
+ yaml.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlString }
|
|
|
+
|
|
|
+constructor TYamlString.Create(const aValue: string);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fValue := aValue;
|
|
|
+ fIsNull := False;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlString.Create;
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fIsNull := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlString.IsNull: Boolean;
|
|
|
+begin
|
|
|
+ Result := fIsNull;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlString.IsScalar: Boolean;
|
|
|
+begin
|
|
|
+ Result := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlString.AsString: string;
|
|
|
+begin
|
|
|
+ Result := fValue;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlString.Value: TFlexValue;
|
|
|
+begin
|
|
|
+ Result := fValue;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlInteger }
|
|
|
+
|
|
|
+constructor TYamlInteger.Create(const aValue: Integer);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fValue := aValue;
|
|
|
+ fIsNull := False;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlInteger.Create;
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fIsNull := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlInteger.IsNull: Boolean;
|
|
|
+begin
|
|
|
+ Result := fIsNull;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlInteger.IsScalar: Boolean;
|
|
|
+begin
|
|
|
+ Result := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlInteger.AsString: string;
|
|
|
+begin
|
|
|
+ Result := IntToStr(fValue);
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlInteger.Value: TFlexValue;
|
|
|
+begin
|
|
|
+ Result := fValue;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlFloat }
|
|
|
+
|
|
|
+constructor TYamlFloat.Create(const aValue: Double);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fValue := aValue;
|
|
|
+ fIsNull := False;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlFloat.Create;
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fIsNull := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlFloat.IsNull: Boolean;
|
|
|
+begin
|
|
|
+ Result := fIsNull;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlFloat.IsScalar: Boolean;
|
|
|
+begin
|
|
|
+ Result := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlFloat.AsString: string;
|
|
|
+begin
|
|
|
+ Result := FloatToStr(fValue);
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlFloat.Value: TFlexValue;
|
|
|
+begin
|
|
|
+ Result := fValue;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlPair }
|
|
|
+
|
|
|
+constructor TYamlPair.Create(const aName: string; const aValue: TYamlValue);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fName := aName;
|
|
|
+ fValue := aValue;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlPair.Create(const aName, aValue: string);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fName := aName;
|
|
|
+ fValue := TYamlString.Create(aValue);
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlPair.Create(const aName: string; const aValue: Double);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fName := aName;
|
|
|
+ fValue := TYamlFloat.Create(aValue);
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlPair.Create(const aName: string; const aValue: Integer);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fName := aName;
|
|
|
+ fValue := TYamlInteger.Create(aValue);
|
|
|
+end;
|
|
|
+
|
|
|
+destructor TYamlPair.Destroy;
|
|
|
+begin
|
|
|
+ if (fValue <> nil) and fValue.Owned then FreeAndNil(fValue);
|
|
|
+ inherited Destroy;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlPair.ToYaml: string;
|
|
|
+var
|
|
|
+ isscalar : Boolean;
|
|
|
+begin
|
|
|
+ if fValue = nil then Exit('null');
|
|
|
+
|
|
|
+ if fValue is TYamlObject then Result := TYamlObject(fValue).ToYaml
|
|
|
+ else if fValue is TYamlArray then Result := TYamlArray(fValue).ParseToYaml(0,isscalar)
|
|
|
+ else Result := Format('%s: %s',[fName,fValue.Value.AsString]);
|
|
|
+
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TYamlPair.AddDescendant(const aDescendent: TYamlAncestor);
|
|
|
+begin
|
|
|
+ if fName = '' then
|
|
|
+ fName := TYamlString(aDescendent).Value
|
|
|
+ else if fValue = nil then
|
|
|
+ fValue:= TYamlValue(aDescendent)
|
|
|
+ else inherited AddDescendant(aDescendent);
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlObject.TEnumerator }
|
|
|
+
|
|
|
+constructor TYamlObject.TEnumerator.Create(const aObject: TYamlObject);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fIndex := -1;
|
|
|
+ fObject := aObject;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.TEnumerator.GetCurrent: TYamlPair;
|
|
|
+begin
|
|
|
+ {$IFNDEF FPC}
|
|
|
+ Result := fObject.fMembers.List[fIndex];
|
|
|
+ {$ELSE}
|
|
|
+ Result := fObject.fMembers.Items[fIndex];
|
|
|
+ {$ENDIF}
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlObject.TEnumerator.MoveNext: Boolean;
|
|
|
+begin
|
|
|
+ Inc(fIndex);
|
|
|
+ Result := fIndex < fObject.Count;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+{ TYamlValue }
|
|
|
+
|
|
|
+function TYamlValue.Value: TFlexValue;
|
|
|
+begin
|
|
|
+ Result := '';
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlArray.TEnumerator }
|
|
|
+
|
|
|
+constructor TYamlArray.TEnumerator.Create(const aArray: TYamlArray);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fIndex := -1;
|
|
|
+ fArray := aArray;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlArray.TEnumerator.GetCurrent: TYamlValue;
|
|
|
+begin
|
|
|
+ {$IFNDEF FPC}
|
|
|
+ Result := fArray.fElements.List[fIndex];
|
|
|
+ {$ELSE}
|
|
|
+ Result := fArray.fElements.Items[fIndex];
|
|
|
+ {$ENDIF}
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlArray.TEnumerator.MoveNext: Boolean;
|
|
|
+begin
|
|
|
+ Inc(fIndex);
|
|
|
+ Result := fIndex < fArray.Count;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlArray }
|
|
|
+
|
|
|
+procedure TYamlArray.AddDescendant(const aDescendant: TYamlAncestor);
|
|
|
+begin
|
|
|
+ fElements.Add(TYamlValue(aDescendant));
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlArray.Create;
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fElements := TList<TYamlValue>.Create;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlArray.Create(const aFirstElem: TYamlValue);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ AddElement(aFirstElem);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TYamlArray.AddElement(const aElement: TYamlValue);
|
|
|
+begin
|
|
|
+ if aElement <> nil then AddDescendant(aElement);
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlArray.AsString: string;
|
|
|
+var
|
|
|
+ first : Boolean;
|
|
|
+ element : TYamlValue;
|
|
|
+begin
|
|
|
+ first := True;
|
|
|
+ for element in fElements do
|
|
|
+ begin
|
|
|
+ if first then Result := Result + element.AsString
|
|
|
+ else Result := Result + ',' + element.AsString;
|
|
|
+ end;
|
|
|
+ Result := Format('[%s]',[Result]);
|
|
|
+end;
|
|
|
+
|
|
|
+destructor TYamlArray.Destroy;
|
|
|
+var
|
|
|
+ element: TYamlAncestor;
|
|
|
+ i: Integer;
|
|
|
+begin
|
|
|
+ if Assigned(fElements) then
|
|
|
+ for i := 0 to fElements.Count - 1 do
|
|
|
+ begin
|
|
|
+ element := fElements[i];
|
|
|
+ if Assigned(element) and (element.Owned) then element.Free;
|
|
|
+ end;
|
|
|
+ if Assigned(fElements) then FreeAndNil(fElements);
|
|
|
+ inherited Destroy;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlArray.GetCount: Integer;
|
|
|
+begin
|
|
|
+ Result := fElements.Count;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlArray.GetEnumerator: TEnumerator;
|
|
|
+begin
|
|
|
+ Result := TEnumerator.Create(Self);
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlArray.GetValue(const aIndex: Integer): TYamlValue;
|
|
|
+begin
|
|
|
+ Result := fElements[aIndex];
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlArray.ParseToYaml(aIndent : Integer; var vIsScalar : Boolean) : string;
|
|
|
+var
|
|
|
+ element : TYamlValue;
|
|
|
+ yaml : TYamlWriter;
|
|
|
+ yvalue : TYamlAncestor;
|
|
|
+ indent : string;
|
|
|
+ isscalar : Boolean;
|
|
|
+begin
|
|
|
+ Result := '';
|
|
|
+ yvalue := nil;
|
|
|
+ yaml := TYamlWriter.Create;
|
|
|
+ try
|
|
|
+ indent := StringOfChar(' ',aIndent);
|
|
|
+ if fElements.Count = 0 then
|
|
|
+ begin
|
|
|
+ vIsScalar := True;
|
|
|
+ Exit('[]');
|
|
|
+ end;
|
|
|
+ for element in fElements do
|
|
|
+ begin
|
|
|
+ yvalue := element;
|
|
|
+ if yvalue is TYamlPair then yvalue := TYamlPair(yvalue).value;
|
|
|
+
|
|
|
+ if yvalue.IsScalar then
|
|
|
+ begin
|
|
|
+ {$IFNDEF FPC}
|
|
|
+ if Result = '' then Result := element.AsString
|
|
|
+ else Result := Result + ', ' + element.AsString;
|
|
|
+ {$ELSE}
|
|
|
+ if Result = '' then Result := TYamlPair(element).Value.AsString
|
|
|
+ else Result := Result + ', ' + TYamlPair(element).Value.AsString;
|
|
|
+ {$ENDIF}
|
|
|
+ end
|
|
|
+ else if (yvalue is TYamlObject) then
|
|
|
+ begin
|
|
|
+ yaml.Write(indent + '- ' + (yvalue as TYamlObject).ParseToYaml(aIndent + NUM_INDENT).TrimLeft);
|
|
|
+ end
|
|
|
+ else if (yvalue is TYamlArray) then
|
|
|
+ begin
|
|
|
+ yaml.Write(Format('%s%s',[indent,(yvalue as TYamlArray).ParseToYaml(aIndent + NUM_INDENT,isscalar)]))
|
|
|
+ end;
|
|
|
+ yaml.Writeln('');
|
|
|
+ end;
|
|
|
+ if (yvalue <> nil) and (yvalue.IsScalar) then
|
|
|
+ begin
|
|
|
+ Result := '[' + Result + ']';
|
|
|
+ vIsScalar := True;
|
|
|
+ end
|
|
|
+ else Result := yaml.Text;
|
|
|
+ finally
|
|
|
+ yaml.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlWriter }
|
|
|
+
|
|
|
+procedure TYamlWriter.Write(const aValue: string);
|
|
|
+begin
|
|
|
+ fData := fData + aValue;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TYamlWriter.Writeln(const aValue: string);
|
|
|
+begin
|
|
|
+ fData := fData + aValue + CRLF;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlWriter.Create;
|
|
|
+begin
|
|
|
+ fData := '';
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlNull }
|
|
|
+
|
|
|
+function TYamlNull.IsNull: Boolean;
|
|
|
+begin
|
|
|
+ Result := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlNull.AsString: string;
|
|
|
+begin
|
|
|
+ Result := 'null';
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlNull.Value: TFlexValue;
|
|
|
+begin
|
|
|
+ Result := nil;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlBoolean }
|
|
|
+
|
|
|
+constructor TYamlBoolean.Create;
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fIsNull := True;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlBoolean.Create(const aValue: Boolean);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fIsNull := False;
|
|
|
+ fValue := aValue;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlBoolean.IsNull: Boolean;
|
|
|
+begin
|
|
|
+ Result := fIsNull;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlBoolean.IsScalar: Boolean;
|
|
|
+begin
|
|
|
+ Result := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlBoolean.AsString: string;
|
|
|
+begin
|
|
|
+ Result := BoolToStr(fValue,True).ToLower;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlBoolean.Value: TFlexValue;
|
|
|
+begin
|
|
|
+ Result := fValue;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TYamlComment }
|
|
|
+
|
|
|
+function TYamlComment.AsString: string;
|
|
|
+begin
|
|
|
+ Result := fValue;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlComment.Create;
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fIsNull := True;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TYamlComment.Create(const aComment: string);
|
|
|
+begin
|
|
|
+ inherited Create;
|
|
|
+ fIsNull := False;
|
|
|
+ fValue := aComment;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlComment.IsNull: Boolean;
|
|
|
+begin
|
|
|
+ Result := fIsNull;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlComment.IsScalar: Boolean;
|
|
|
+begin
|
|
|
+ Result := True;
|
|
|
+end;
|
|
|
+
|
|
|
+function TYamlComment.Value: TFlexValue;
|
|
|
+begin
|
|
|
+
|
|
|
+end;
|
|
|
+
|
|
|
+end.
|