It is easy to read and use.
Yaml parser was build to give more possibilities than ini.
You can create Yaml database from text as well as from file, you can add, set get, delete keys, clone the database, insert existing sets of data to an existing database and much more.
If you never heard of yaml before, take a look here: Can you learn Yaml in five minutes?
:!: Note :!: - not all yaml syntax is suported.
DocumentationA subkey must be indented by 2 spaces (not Tab). If a key has got subkeys, it must not have values :!:
MainKey: SubKey: SubSubKey1: value1 SubSubKey2: value2 SubSubKeyList: - item1 - item2 [color=red]'[/color]Subkey with non alphanumeric characters[color=red]'[/color]: "multiple lines value"Simple example:
yamlText= ;example yaml data ( `%yaml --- testdata, this is a comment only Settings: Size: width: 1 height: 2 Colors: background: blue foreground: black text: white ButtonText: Button1: AutoHotkey Button2: OK ) yml:=Yaml_Init(yamlText) ;create database from text MsgBox % Yaml_Get(yml,"Settings.Colors.background") ;get a key Yaml_Add(yml,"Settings.ButtonText.Button3","Exit") ;add a key MsgBox % Yaml_Save(yml) ;Show dump of databaseAdvanced Example
yamlText= ;example yaml data ( `%yaml --- testdata, this is a comment only Settings: Size: width: 1 height: 2 Colors: background: blue foreground: black text: white ButtonText: Button1: AutoHotkey Button2: OK 'Test with space': item: value ) y:=Yaml_Init("") ;create empty Yaml database Loop 2 ;a new Key will be added to the database and above data will be appended/inserted Yaml_Add(y,"yaml" A_Index),Yaml_Insert(y,YamlText,"yaml" A_Index) Loop,2 ;read some data from each key MsgBox % Yaml_Get(y,"yaml" A_Index ".Settings.ButtonText.Button1") MsgBox % Yaml_Dump(y,var) ;dump the database to a variable MsgBox % Yaml_Save(y,"x:\Settings.yml") ;save database to a file ExitAppYaml to Gui
yaml= ( Gui: Add: Button: Test: - x0 y0 w100 h30 gTest Hello: - x200 y30 xs w200 h25 gTest ListView: 'Test|new|other': - w200 r20 gTest vListView ) y:=Yaml_Init(yaml) Loop % Yaml_Get(y,"Gui.0"){ p1:=Yaml_Get(y,"Gui." A_Index) Loop % Yaml_Get(y,"Gui." p1 "." 0){ p2:=Yaml_Get(y,"Gui." p1 "." A_Index) Loop % Yaml_Get(y,"Gui." p1 "." p2 ".0") { p4:=Yaml_Get(y,"Gui." p1 "." p2 "." A_Index) p3:=Yaml_Get(y,"Gui." p1 "." p2 "." Yaml_Get(y,"Gui." p1 "." p2 "." A_Index) ".1") Gui,%p1%,%p2%,%p3%,%p4% } } } LV_Add("","test","test","test") Gui,Show Return GuiClose: ExitApp Test: MsgBox Return
Functions
Yaml_Add(pYaml,key1="",key2=""){ If (recursive:=InStr(key1,".")=1 ? 1 : 0) StringTrimLeft,key1,key1,1 If InStr(key1,".") parent:=SubStr(key1,1,InStr(key1,".",1,0)-1) StringTrimLeft,key,key1,% InStr(key1,".",1,0) If (parent!="" && !Yaml_Exist(pYaml,key1)) Yaml_Add(pYaml,"." parent,key) If !Yaml_Exist(pYaml,key1){ count:=Yaml_Get(pYaml,parent!="" ? (parent "." 0) : 0) count++ Yaml_Assign(pYaml,parent!="" ? (parent "." 0) : 0,count) Yaml_Assign(pYaml,parent!="" ? (parent "." count) : count,key) } else if (!recursive){ count:=Yaml_Get(pYaml,key1 "." 0) If (count=""){ count=2 Yaml_Assign(pYaml,key1 ".1",Yaml_Get(pYaml,key1)) } else count++ Yaml_Assign(pYaml,key1 "." 0,count) Yaml_Assign(pYaml,key1 "." count,key2) } key2:=(RegExMatch(Key2,"^\s?\w+\s?$") ? key2 : "'" key2 "'") Yaml_Assign(pYaml,key1,Yaml_Get(pYaml,key1)="" ? key2 : Yaml_Get(pYaml,key1) "," key2) } Yaml_Exist(pYaml,Key=""){ VarSetCapacity(var,16,0),NumPut(8, var) If A_IsUnicode wStr:=Key else VarSetCapacity(wStr, StrLen(Key)*2+1,0) ,DllCall("MultiByteToWideChar", "UInt",0, "UInt",0, "UInt",&Key, "Int",-1, "UInt",&wStr, "Int",StrLen(Key)+1) NumPut(DllCall("oleaut32\SysAllocString","Str",wStr),var,8) DllCall(NumGet(NumGet(pYaml+0)+48), "UInt",pYaml, "UInt",&var, "IntP",bExist) DllCall("oleaut32\SysFreeString", "UInt",NumGet(var,8)),NumPut(0,var,8) Return bExist } Yaml_Dump(pYaml,ByRef Output,key=""){ If (key=""){ Loop % Yaml_Get(pYaml,0) { mainkey:=Yaml_Get(pYaml,A_Index) Output .= Yaml_Get(pYaml,mainkey ".")="" ? "" : Yaml_Get(pYaml,mainkey ".") "`n" ;"---`r`n" value:=Yaml_Get(pYaml,mainkey) Output .= (RegExMatch(mainkey,"^\w+$") ? mainkey : ("'" mainkey "'")) ":" if (sub:=Yaml_Get(pYaml,mainkey "." 1)){ if !Yaml_Exist(pYaml,mainkey "." sub){ Output .= "`n" Loop % Yaml_Get(pYaml,mainkey ".0"){ value:=Yaml_Get(pYaml,mainkey "." A_Index) Output .= " - " (RegExMatch(value,"^\w+$") ? value : "'" value "'") "`n" } Continue } else Output .= "`n" } else Output .= " " value "`n" Yaml_Dump(pYaml,Output,"." mainkey) } return Output } else { If (-1 < (start:=RegExMatch(key,"[^\.]")-1)){ Loop % start*2 recurse .= A_Space StringTrimLeft,_key,key,%start% } else _key:=key Loop % Yaml_Get(pYaml,_key "." 0){ key1:=Yaml_Get(pYaml,_key "." A_Index) Output .= Yaml_Get(pYaml,_key "." key1 ".")="" ? "" : Yaml_Get(pYaml,_key "." key1 ".") "`n" value:=Yaml_Get(pYaml,_key "." key1) Output .= recurse (RegExMatch(key1,"^\w+$") ? key1 : ("'" key1 "'")) ":" if (sub:=Yaml_Get(pYaml,_key "." key1 ".1")){ if !Yaml_Exist(pYaml,_key "." key1 "." sub){ Output .= "`n" Loop % Yaml_Get(pYaml,_key "." key1 ".0"){ value:=Yaml_Get(pYaml,_key "." key1 "." A_Index) Output .= recurse " - " value "`n" } Continue } else Output .= "`n" } else Output .= " " value "`n" Yaml_Dump(pYaml,Output, "." key "." key1) } } Return output } Yaml_Save(pYaml,ToFile=""){ Yaml_Dump(pYaml,file) If (ToFile!="" && FileExist(ToFile)){ StringReplace,savefile,file,`n,`r`n,A FileMove,%ToFile%,%ToFile%.bkp.yaml If FileExist(ToFile){ MsgBox Error Saving File } FileAppend,%savefile%,%ToFile% If (ErrorLevel){ MsgBox Error Saving File } FileDelete,%ToFile%.bkp.yaml If (ErrorLevel){ MsgBox Error Saving File FileDelete,%ToFile%.bkp FileMove,%ToFile%.bkp.yaml,%ToFile% } } else if (ToFile!=""){ StringReplace,savefile,file,`n,`r`n,A FileAppend,%savefile%,%ToFile% If (ErrorLevel){ MsgBox Error Saving File } } Return file } Yaml_Clone(pYaml){ static CLSIDString:="{EE09B103-97E0-11CF-978F-00A02463E06F}", IIDString:="{42C642C1-97E1-11CF-978F-00A02463E06F}", StrGet:="StrGet" If (!Init && Init:=1){ ;Initialize COM and create database DllCall("ole32\CoInitialize", "UInt",0),VarSetCapacity(var1, 16),VarSetCapacity(var2, 16) NumPut(8, var1),NumPut(8, var2),VarSetCapacity(CLSID, 16),VarSetCapacity(wKey, 79),VarSetCapacity(IID, 16) If A_IsUnicode DllCall("ole32\CLSIDFromString", "Str",CLSIDString, "Str",CLSID) ,DllCall("ole32\CLSIDFromString", "Str",IIDString, "Str",IID) else DllCall("MultiByteToWideChar", "UInt",0, "UInt",0, "UInt",&CLSIDString, "Int",-1, "UInt",&wKey, "Int",39) ,DllCall("ole32\CLSIDFromString", "Str",wKey, "Str",CLSID) ,DllCall("MultiByteToWideChar", "UInt",0, "UInt",0, "UInt",&IIDString, "Int",-1, "UInt",&wKey, "Int",39) ,DllCall("ole32\CLSIDFromString", "Str",wKey, "Str",IID) } DllCall("ole32\CoCreateInstance", "Str",CLSID, "UInt",0, "UInt",5, "Str",IID, "UIntP",_pYaml) ; CLSCTX=5 ,DllCall(NumGet(NumGet(_pYaml+0)+72), "UInt",_pYaml, "Int",1) VarSetCapacity(var1, 16),VarSetCapacity(var2, 16),NumPut(8, var1),NumPut(8, var2) DllCall(NumGet(NumGet(pYaml+0)+80), "UInt",pYaml, "UIntP",penum) ; create key-list in penum Loop { If (DllCall(NumGet(NumGet(penum+0)+12), "UInt",penum, "UInt",1, "UInt",&var1, "UInt",0)) { DllCall(NumGet(NumGet(penum+0)+8), "UInt",penum) ; END: destroy key-list penum= ; signal end of list ErrorLevel= ; empty Return _pYaml } If A_IsUnicode wStr:=%StrGet%(NumGet(var1,8)) else nLen := DllCall("WideCharToMultiByte", "UInt",0, "UInt",0, "UInt",NumGet(var1,8), "Int",-1, "UInt",0, "Int",0, "UInt",0, "UInt",0) ,VarSetCapacity(wKey, nLen) ,DllCall("WideCharToMultiByte", "UInt",0, "UInt",0, "UInt",NumGet(var1,8), "Int",-1, "Str",wKey, "Int",nLen, "UInt",0, "UInt",0) DllCall("oleaut32\SysFreeString", "UInt",NumGet(var1,8)),NumPut(0,var1,8) Yaml_Assign(_pYaml,wKey,Yaml_Get(pYaml,wKey)) } } Yaml_Get(pYaml,key1=""){ static StrGet:="StrGet" VarSetCapacity(var1, 16),VarSetCapacity(var2, 16),NumPut(8, var1),NumPut(8, var2) If A_IsUnicode wStr:=key1 else VarSetCapacity(wStr, StrLen(Key1)*2+1,0) ,DllCall("MultiByteToWideChar", "UInt",0, "UInt",0, "UInt",&Key1, "Int",-1, "UInt",&wStr, "Int",StrLen(Key1)+1) NumPut(DllCall("oleaut32\SysAllocString","Str",wStr),var1,8) DllCall(NumGet(NumGet(pYaml+0)+48), "UInt",pYaml, "UInt",&var1, "IntP",bExist) If bExist { DllCall(NumGet(NumGet(pYaml+0)+36), "UInt",pYaml, "UInt",&var1, "UInt",&var2) If (NumGet(var2,8)=0xcdcdcdcd) ;uninitialized = empty string Return If A_IsUnicode Key2:=%StrGet%(NumGet(var2,8)) else nLen := DllCall("WideCharToMultiByte", "UInt",0, "UInt",0, "UInt",NumGet(var2,8), "Int",-1, "UInt",0, "Int",0, "UInt",0, "UInt",0) ,VarSetCapacity(Key2, nLen,0) ,DllCall("WideCharToMultiByte", "UInt",0, "UInt",0, "UInt",NumGet(var2,8), "Int",-1, "Str",Key2, "Int",nLen, "UInt",0, "UInt",0) DllCall("oleaut32\SysFreeString", "UInt",NumGet(var2,8)),NumPut(0,var2,8) } DllCall("oleaut32\SysFreeString", "UInt",NumGet(var1,8)),NumPut(0,var1,8) Return Key2 } Yaml_Set(pYaml,key1="",key2=""){ If !Yaml_Exist(pYaml,key1){ Yaml_Add(pYaml,key1,key2) return } Yaml_Assign(pYaml,key1,key2) Return } Yaml_Assign(pYaml,key1,key2=""){ Loop % (2){ VarSetCapacity(var%A_Index%,16,0),NumPut(8,var%A_Index%) If A_IsUnicode wStr:=Key%A_Index% else VarSetCapacity(wStr, StrLen(Key%A_Index%)*2+1,0) ,DllCall("MultiByteToWideChar", "UInt",0, "UInt",0, "UInt",&Key%A_Index%, "Int",-1, "UInt",&wStr, "Int",StrLen(Key%A_Index%)+1) NumPut(DllCall("oleaut32\SysAllocString","Str",wStr),var%A_Index%,8) } DllCall(NumGet(NumGet(pYaml+0)+32), "UInt",pYaml, "UInt",&var1, "UInt",&var2) ; 8 (Set0 -> 7) Loop 2 DllCall("oleaut32\SysFreeString", "UInt",NumGet(var%A_Index%,8)),NumPut(0,var%A_Index%,8) } Yaml_Insert(_pYaml,Yaml_File,MainItem0){ If FileExist(Yaml_File){ FileRead,Yaml_File_,%Yaml_File% If (ErrorLevel){ MsgBox Error Reading File Return } } else Yaml_File_:=Yaml_File Loop,Parse,MainItem0,. { space.=A_Space A_Space _depth:=A_Index-1 MainItem%_depth%:=A_LoopField } Loop,Parse,Yaml_File_,`n,`r { If A_LoopField= Continue if !create Key1:="",Key2:="",Key3:="",Item:="" If (!create){ RegExMatch(A_LoopField,"^\s+-\s(.*)$",Key) If (Key1!=""){ count:=Yaml_Get(_pYaml,LastItem ".0") count++ Yaml_Assign(_pYaml,LastItem "." count,Key1) Yaml_Assign(_pYaml,LastItem ".0", count) item:=Yaml_Get(_pYaml,LastItem) Yaml_Assign(_pYaml,LastItem,item="" ? Key1 : item "," key1) Continue } Loop 3 Key%A_Index%= RegExMatch(A_LoopField,"^(\s*)(\w+)\s?:\s?(.*)\s?$",Key) If (Key2=""){ Loop 3 Key%A_Index%= RegExMatch(A_LoopField,"^(\s*)'(.+)':\s?(.*)\s?$",Key) } If (Key2=""){ LastLine.=Space A_LoopField "`n" Continue } If (SubStr(LastLine,0)="`n") StringTrimRight,LastLine,LastLine,1 depth:=Round(Strlen(Key1)/2,0)+_depth+1 MainItem%depth%:=Key2 Item:=MainItem0 While % ((i:=A_Index) && depth>A_Index) Item.= "." . MainItem%i% If !Yaml_Exist(_pYaml,Item "." key2){ MainItem:=Yaml_Get(_pYaml,Item) count:=Yaml_Get(_pYaml,Item . ".0") count++ Yaml_Assign(_pYaml,Item . ".0",count) Yaml_Assign(_pYaml,Item . "." . count,key2) Yaml_Assign(_pYaml,Item,MainItem . (MainItem="" ? "" : ",") . (RegExMatch(Key2,"^\s?\w+\s?$") ? key2 : "'" key2 "'")) } Item.="." . key2 Yaml_Assign(_pYaml,Item,key3) LastItem:=Item If LastLine!= Yaml_Assign(_pYaml,Item . ".",LastLine),LastLine:="" } else Yaml_Assign(_pYaml,Item,Yaml_Get(_pYaml,Item) . A_LoopField) If RegExMatch(Key3,"^\s*""") create:=1 if (create && RegExMatch(A_LoopField,"""\s*$")) create:=0 } } Yaml_DeleteAll(pYaml){ DllCall(NumGet(NumGet(pYaml+0)+68), "UInt",pYaml) ;delete all keys } Yaml_DeleteItem(pYaml,key){ VarSetCapacity(var1,16,0),NumPut(8,var1) If A_IsUnicode wStr:=key else VarSetCapacity(wStr, StrLen(Key)*2+1,0) ,DllCall("MultiByteToWideChar", "UInt",0, "UInt",0, "UInt",&Key, "Int",-1, "UInt",&wStr, "Int",StrLen(Key)+1) NumPut(DllCall("oleaut32\SysAllocString","Str",wStr),var1,8) DllCall(NumGet(NumGet(pYaml+0)+64), "UInt",pYaml, "UInt",&var1) DllCall("oleaut32\SysFreeString", "UInt",NumGet(var1,8)),NumPut(0,var1,8) } Yaml_Delete(pYaml,key){ Yaml_DeleteItem(pYaml,key) MainItem:=SubStr(key,1,InStr(key,".",1,0)-1) If !(count:=Yaml_Get(pYaml,MainItem ".0")) Return Key:=SubStr(key,InStr(key,".",1,0)+1) Yaml_Set(pYaml,MainItem,RegExReplace(Yaml_Get(pYaml,MainItem),"^" key "$|^" key ",|," key "|" key "$","")) Loop % count If (Key=Yaml_Get(pYaml,MainItem "." A_Index)){ Yaml_Set(pYaml,MainItem "." A_Index,Yaml_Get(pYaml,MainItem "." count)) Yaml_DeleteItem(pYaml,MainItem "." count) Yaml_Set(pYaml,MainItem ".0",Yaml_Get(pYaml,MainItem ".0")-1) break } } Yaml_Init(Yaml_File="?",pointerYaml=""){ static pYaml, CLSID,IID,Init,FileIndex static CLSIDString:="{EE09B103-97E0-11CF-978F-00A02463E06F}", IIDString:="{42C642C1-97E1-11CF-978F-00A02463E06F}" If (!Init && Init:=1){ ;Initialize COM and create database DllCall("ole32\CoInitialize", "UInt",0),VarSetCapacity(var1, 16),VarSetCapacity(var2, 16) NumPut(8, var1),NumPut(8, var2),VarSetCapacity(CLSID, 16),VarSetCapacity(wKey, 79),VarSetCapacity(IID, 16) If A_IsUnicode DllCall("ole32\CLSIDFromString", "Str",CLSIDString, "Str",CLSID) ,DllCall("ole32\CLSIDFromString", "Str",IIDString, "Str",IID) else DllCall("MultiByteToWideChar", "UInt",0, "UInt",0, "UInt",&CLSIDString, "Int",-1, "UInt",&wKey, "Int",39) ,DllCall("ole32\CLSIDFromString", "Str",wKey, "Str",CLSID) ,DllCall("MultiByteToWideChar", "UInt",0, "UInt",0, "UInt",&IIDString, "Int",-1, "UInt",&wKey, "Int",39) ,DllCall("ole32\CLSIDFromString", "Str",wKey, "Str",IID) DllCall("ole32\CoCreateInstance", "Str",CLSID, "UInt",0, "UInt",5, "Str",IID, "UIntP",pYaml) ; CLSCTX=5 DllCall(NumGet(NumGet(pYaml+0)+72), "UInt",pYaml, "Int",0) ; Set compare mode binary (casesensitive) } If (Yaml_File="?"){ Loop % Yaml_Get(pYaml,0) Yaml_Save(Yaml_Get(pYaml,Yaml_Get(pYaml,A_Index)),Yaml_Get(pYaml,A_Index)) Return } Loop,%Yaml_File% Yaml_File:=A_LoopFileLongPath If (!Yaml_Exist(_pYaml,Yaml_File) && !Yaml_Exist(pointerYaml)) DllCall("ole32\CoCreateInstance", "Str",CLSID, "UInt",0, "UInt",5, "Str",IID, "UIntP",_pYaml) ; CLSCTX=5 ,DllCall(NumGet(NumGet(_pYaml+0)+72), "UInt",_pYaml, "Int",1) ; Set compare mode text (caseinsensitive) else if (pointerYaml) _pYaml:=pointerYaml else return Yaml_Get(pYaml,Yaml_File) If FileExist(Yaml_File){ FileRead,Yaml_File_,%Yaml_File% If (ErrorLevel){ MsgBox Error Reading File Return } FileIndex++ Yaml_Assign(pYaml,0,FileIndex) Yaml_Assign(pYaml,FileIndex,Yaml_File) Yaml_Assign(pYaml,Yaml_File,_pYaml) } else Yaml_File_:=Yaml_File Loop,Parse,Yaml_File_,`n,`r { If A_LoopField= Continue if !create Key1:="",Key2:="",Key3:="",Item:="" If (MainItem0="" || (!create && !RegExMatch(A_LoopField,"^\s"))){ If !RegExMatch(A_LoopField,"^(\w+)\s?:\s?(.*)\s*$",MainItem) If !RegExMatch(A_LoopField,"^'(.+)'\s?:\s?(.*)\s*$",MainItem){ Key1= RegExMatch(A_LoopField,"^\s+-\s(.*)$",Key) If (Key1!=""){ count:=Yaml_Get(_pYaml,MainItem0 ".0") count++ Yaml_Assign(_pYaml,MainItem0 "." count,Key1) Yaml_Assign(_pYaml,MainItem0 ".0", count) item:=Yaml_Get(_pYaml,MainItem0) Yaml_Assign(_pYaml,MainItem0,item="" ? Key1 : item "," key1) Continue } LastLine.=A_LoopField "`n" Continue } MainItem0:=MainItem1 ItemCount++ MainItem:=Yaml_Get(_pYaml,"") . (Yaml_Get(_pYaml,"")!="" ? "," : "") . (RegExMatch(MainItem0,"^\s?\w+\s?$") ? MainItem0 : "'" MainItem0 "'") Yaml_Assign(_pYaml,"",MainItem) Yaml_Assign(_pYaml,0,ItemCount) Yaml_Assign(_pYaml,ItemCount,MainItem0) Yaml_Assign(_pYaml,MainItem0,MainItem2) If (SubStr(LastLine,0)="`n") StringTrimRight,LastLine,LastLine,1 Yaml_Assign(_pYaml,MainItem0 ".",LastLine) MainItem:="",MainItem1:="",MainItem2:="",LastLine:="" LastItem:=MainItem0 Continue } If (!create){ Key1= RegExMatch(A_LoopField,"^\s+-\s(.*)$",Key) If (Key1!=""){ count:=Yaml_Get(_pYaml,LastItem ".0") count++ Yaml_Assign(_pYaml,LastItem "." count,Key1) Yaml_Assign(_pYaml,LastItem ".0", count) item:=Yaml_Get(_pYaml,LastItem) Yaml_Assign(_pYaml,LastItem,item="" ? Key1 : item "," key1) Continue } Loop 3 Key%A_Index%= RegExMatch(A_LoopField,"^(\s+)(\w+)\s?:\s?(.*)\s?$",Key) If (Key2=""){ Loop 3 Key%A_Index%= RegExMatch(A_LoopField,"^(\s+)'(.+)':\s?(.*)\s?$",Key) } If (Key2=""){ LastLine.=A_LoopField "`n" Continue } If (SubStr(LastLine,0)="`n") StringTrimRight,LastLine,LastLine,1 depth:=Round(Strlen(Key1)/2,0) MainItem%depth%:=Key2 Item:=MainItem0 While % ((i:=A_Index) && depth>A_Index) Item.= "." . MainItem%i% If !Yaml_Exist(_pYaml,Item "." key2){ MainItem:=Yaml_Get(_pYaml,Item) count:=Yaml_Get(_pYaml,Item . ".0") count++ Yaml_Assign(_pYaml,Item . ".0",count) Yaml_Assign(_pYaml,Item . "." . count,key2) Yaml_Assign(_pYaml,Item,MainItem . (MainItem="" ? "" : ",") . (RegExMatch(Key2,"^\s?\w+\s?$") ? key2 : "'" key2 "'")) } Item.="." . key2 Yaml_Assign(_pYaml,Item,key3) LastItem:=Item If LastLine!= Yaml_Assign(_pYaml,Item . ".",LastLine),LastLine:="" } else Yaml_Assign(_pYaml,Item,Yaml_Get(_pYaml,Item) . A_LoopField) If RegExMatch(Key3,"^\s*""") create:=1 if (create && RegExMatch(A_LoopField,"""\s*$")) create:=0 } Return _pYaml }