|
@@ -0,0 +1,1032 @@
|
|
|
+{ ***************************************************************************
|
|
|
+
|
|
|
+ Copyright (c) 2015-2019 Kike Pérez
|
|
|
+
|
|
|
+ Unit : Quick.YAML
|
|
|
+ Description : YAML Object parser
|
|
|
+ Author : Kike Pérez
|
|
|
+ Version : 1.0
|
|
|
+ Created : 17/04/2019
|
|
|
+ Modified : 27/04/2019
|
|
|
+
|
|
|
+ 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,
|
|
|
+ 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; virtual;
|
|
|
+ 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; virtual;
|
|
|
+ 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; virtual;
|
|
|
+ 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; virtual;
|
|
|
+ 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 InSameLevel(const aValue1, aValue2 : string) : Boolean;
|
|
|
+ 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;
|
|
|
+ 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;
|
|
|
+ end;
|
|
|
+
|
|
|
+ EYAMLException = class(Exception);
|
|
|
+
|
|
|
+implementation
|
|
|
+
|
|
|
+const
|
|
|
+ CRLF = #13#10;
|
|
|
+
|
|
|
+
|
|
|
+{ 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
|
|
|
+ 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;
|
|
|
+
|
|
|
+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;
|
|
|
+begin
|
|
|
+ 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;
|
|
|
+
|
|
|
+function TYamlObject.InSameLevel(const aValue1, aValue2: string): Boolean;
|
|
|
+begin
|
|
|
+ Result := GetItemLevel(aValue1) = GetItemLevel(aValue2);
|
|
|
+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;
|
|
|
+ aitem : string;
|
|
|
+ yamlType : TYamlType;
|
|
|
+begin
|
|
|
+ while yaml.Count > vIndex do
|
|
|
+ begin
|
|
|
+ value := yaml[vIndex].Trim;
|
|
|
+
|
|
|
+ name := ParsePairName(value);
|
|
|
+ if (name.IsEmpty) or (value.IsEmpty) or (value.StartsWith('#')) then Exit(nil)
|
|
|
+ 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;
|
|
|
+
|
|
|
+ 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);
|
|
|
+ level := GetItemLevel(yaml[vIndex]);
|
|
|
+ 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;
|
|
|
+ yamlvalue : TYamlAncestor;
|
|
|
+ vIndex : Integer;
|
|
|
+begin
|
|
|
+ yaml := TList<string>.Create;
|
|
|
+ try
|
|
|
+ vIndex := 0;
|
|
|
+ for line in aData.Split([#13]) do yaml.Add(StringReplace(line,#10,'',[rfReplaceAll]));
|
|
|
+ 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;
|
|
|
+ yamlvalue : TYamlAncestor;
|
|
|
+ vIndex : Integer;
|
|
|
+begin
|
|
|
+ yaml := TList<string>.Create;
|
|
|
+ try
|
|
|
+ vIndex := 0;
|
|
|
+ for line in aData.Split([#13]) do yaml.Add(StringReplace(line,#10,'',[rfReplaceAll]));
|
|
|
+ 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
|
|
|
+ member : TYamlPair;
|
|
|
+ yaml : TYamlWriter;
|
|
|
+ yvalue : TYamlAncestor;
|
|
|
+ indent : string;
|
|
|
+ isscalar : Boolean;
|
|
|
+ rarray : string;
|
|
|
+begin
|
|
|
+ yaml := TYamlWriter.Create;
|
|
|
+ try
|
|
|
+ indent := StringOfChar(' ',aIndent);
|
|
|
+ for member in fMembers do
|
|
|
+ begin
|
|
|
+ yvalue := member.Value;
|
|
|
+ if yvalue.IsScalar then
|
|
|
+ begin
|
|
|
+ if yvalue is TYamlComment then yaml.Writeln(Format('#%s%s',[indent,TYamlComment(member.Value).AsString]))
|
|
|
+ else yaml.Writeln(Format('%s%s: %s',[indent,member.Name,member.Value.Value.AsString]));
|
|
|
+ end
|
|
|
+ else if (yvalue is TYamlObject) then
|
|
|
+ begin
|
|
|
+ yaml.Writeln(Format('%s%s:',[indent,member.Name]));
|
|
|
+ yaml.Write((yvalue as TYamlObject).ParseToYaml(aIndent + 2));
|
|
|
+ end
|
|
|
+ else if (yvalue is TYamlArray) then
|
|
|
+ begin
|
|
|
+ isscalar := False;
|
|
|
+ rarray := (yvalue as TYamlArray).ParseToYaml(aIndent + 2,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
|
|
|
+ AddDescendant(aElement);
|
|
|
+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 := '';
|
|
|
+ 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 + 2).TrimLeft);
|
|
|
+ end
|
|
|
+ else if (yvalue is TYamlArray) then
|
|
|
+ begin
|
|
|
+ yaml.Write(Format('%s%s',[indent,(yvalue as TYamlArray).ParseToYaml(aIndent + 2,isscalar)]))
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ if 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 := fValue.ToString(True);
|
|
|
+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.
|