VERSION 1.0 CLASSBEGINMultiUse = -1 'TrueENDAttribute VB_Name = "YAML"Attribute VB_Base = "0{FCFB3D2A-A0FA-1068-A738-08002B3371B5}"Attribute VB_GlobalNameSpace = FalseAttribute VB_Creatable = FalseAttribute VB_PredeclaredId = FalseAttribute VB_Exposed = FalseAttribute VB_TemplateDerived = FalseAttribute VB_Customizable = FalseOption Explicit' Const yamlPath As String = "\\Mac\iCloud\Development\cv\cv.yml"Public yamlPath As StringPrivate Function RemoveEmptyStrings(arr() As String) As String()Dim tempArray() As StringDim i As Integer, j As Integer: j = 0ReDim tempArray(LBound(arr) To UBound(arr))j = 0For i = LBound(arr) To UBound(arr)If Len(arr(i)) > 0 ThentempArray(j) = arr(i)j = j + 1End IfNext iReDim Preserve tempArray(0 To j - 1)RemoveEmptyStrings = tempArrayEnd FunctionPrivate Function RegexMatch(inputString As String, pattern As String, Optional isGlobal As Boolean = True) As Boolean' checks for regex match without instantiating 80 gazillion objects' parameters' isGlobal: whether the regex check is globalDim Regex As Object: Set Regex = CreateObject("VBScript.RegExp")Regex.pattern = patternRegex.Global = isGlobalRegexMatch = Regex.Test(inputString)End FunctionPrivate Function RegexSplit(inputString As String, pattern As String, Optional onlyFirst As Boolean = False, Optional splitBefore As Boolean = False) As String()' splits array at any pattern that matches a regex' parameters' onlyFirst: if true, only splits the first instance of the match, creating an array of length 2' splitBefore: if true, preserves the actual instance of the matchDim Regex As Object: Set Regex = CreateObject("VBScript.RegExp")Dim matches As ObjectDim match As ObjectDim splitParts() As String: ReDim splitParts(0 To 0)Dim pos As IntegerDim lastPos As Integer: lastPos = 1Dim i As Integer: i = 0' set regex flagsRegex.Global = TrueRegex.IgnoreCase = FalseRegex.pattern = patternSet matches = Regex.Execute(inputString)' lastPos = 1' i = 0For Each match In matchespos = match.FirstIndex + 1ReDim Preserve splitParts(i)splitParts(i) = Mid(inputString, lastPos, pos - lastPos)If splitBefore ThenlastPos = posElselastPos = pos + Len(match.Value)End Ifi = i + 1If onlyFirst Then Exit ForNext matchIf lastPos <= Len(inputString) ThenReDim Preserve splitParts(i)splitParts(i) = Mid(inputString, lastPos)End If' retvrnRegexSplit = RemoveEmptyStrings(splitParts)End FunctionPrivate Function RegexSubstitute(inputString As String, pattern As String, Optional substitution As String = "")' does what it says on the tinDim Regex As Object: Set Regex = CreateObject("VBScript.RegExp")Regex.pattern = patternRegex.IgnoreCase = FalseRegex.Global = TrueRegexSubstitute = Regex.Replace(inputString, substitution)End Function' YAML Layer Parser Pseudocode' ====' function GetYAMLLayerAsCollection(String fromYAML) {' Collection mainDictionary = New Collection();' if (fromYAML.containsRegex(/\n[A-Za-z]/)) {' // is a dictionary' String[] temporaryArray = fromYAML.split(/\n[A-Za-z]/);' for each x in temporaryArray {' x.splitByFirstInstanceOf(':\n');' x[1].replaceAllInstancesOf('' mainDictionary.add(x[0], x[1]);' }' } else if (fromYAML.containsRegex(/\n-/)) {' // if array, process the array and return it as "self"' String[] temporaryArray = fromYAML.splitBy('\n-');' for each x in temporaryArray {' x.removeAllInstancesOf('\n- ');' x.replaceAllInstancesOf('\n ', '\n');' mainDictionary.add("self", temporaryArray);' }' } else if (fromYAML.startsWith('"')) {' mainDictionary.add("self", removeQuotes(fromYAML));' } else {' MsgBox("Processing error: neither array, dictionary, nor string");' }' }Private Function GetYAMLLayerAsDictionary(fromYAML As String) As Scripting.DictionaryDim mainDictionary As Scripting.Dictionary: Set mainDictionary = New Scripting.Dictionary' create regex objects to test for dict, array, and string'Dim regEx_dict As Object: Set regEx_dict = CreateObject("VBScript.RegExp")'Dim regEx_arry As Object: Set regEx_arry = CreateObject("VBScript.RegExp")'Dim regEx_strn As Object: Set regEx_strn = CreateObject("VBScript.RegExp")'regEx_dict.Global = True: regEx_dict.Pattern = "\n[A-Za-z]"'regEx_arry.Global = True: regEx_arry.Pattern = "\n-\s"'regEx_strn.Global = False: regEx_strn.Pattern = "^\s*""(.*?)""\s*$"Dim parts() As StringIf RegexMatch(fromYAML, "(?:\n|\^)\w+:", True) Then' is a dictionaryparts = RegexSplit(fromYAML, "\n\w+:", False, True)Dim part As Variant ' not sure why it can't be as string but whatever billy gatesCall mainDictionary.Add(ID_TYPE, "Dictionary") ' identify as dictFor Each part In partsDim keyValue() As String: keyValue = RegexSplit(CStr(part), ":\s", True)' trim trailing \n from categoryIf UBound(keyValue) > 0 ThenkeyValue(0) = RegexSubstitute(keyValue(0), "^\n+")' trim 2 spaces off of each line if they're therekeyValue(1) = RegexSubstitute(keyValue(1), "^\s{2}")keyValue(1) = RegexSubstitute(keyValue(1), "\n\s{2}", vbLf)Call mainDictionary.Add(keyValue(0), keyValue(1))End IfNext partElseIf RegexMatch(fromYAML, "^-\s", True) Then' is an arrayCall mainDictionary.Add(ID_TYPE, "Array")parts = RegexSplit(fromYAML, "(^|\n)-\s", False)Dim i As IntegerFor i = LBound(parts) To UBound(parts)parts(i) = RegexSubstitute(parts(i), "\n\s{2}", vbLf)Next iCall mainDictionary.Add(ID_SELF, parts)ElseIf RegexMatch(fromYAML, "^\s*""(.*?)""\s*$", True) Then' is a stringCall mainDictionary.Add(ID_TYPE, "String")Call mainDictionary.Add(ID_SELF, RegexSubstitute(fromYAML, """", ""))ElseCall mainDictionary.Add(ID_SELF, "")Debug.Print _"Neither array, dictionary, nor string:" & _vbCrLf & vbCrLf & fromYAML & vbCrLf & vbCrLf & _"Make sure all strings are enclosed in double quotes." ', _'vbOKOnly, "YAML Error")End IfSet GetYAMLLayerAsDictionary = mainDictionaryEnd Function' YAML Traverser Pseudocode' ==='' function TraverseYAML(String fromYAML) {' Dictionary mainDictionary = GetYAMLLayerAsDictionary(fromYAML);' if mainDictionary.___type___ = "Dictionary" {' for each entry in mainDictionary {' TraverseYAML(entry)' }' return mainDictionary;' } else if mainDictionary.___type___ = "Array" {' for each entry in mainDictionary.___self___ {' TraverseYAML(entry)' }' return mainDictionary;' } else if mainDictionary.___type___ = "String" {' return mainDictionary;' } else {' MsgBox("Internal YAML Error")' }' }Private Function GetYAMLAsDictionary(fromYAML As String) As Scripting.DictionaryDim mainDictionary As Object: Set mainDictionary = GetYAMLLayerAsDictionary(fromYAML)Dim entry As VariantIf mainDictionary(ID_TYPE) = "Dictionary" ThenFor Each entry In mainDictionaryDebug.Print "=== PROCESSING DICTIONARY ENTRY ==="Debug.Print entry & " => " & mainDictionary(entry)If entry <> ID_TYPE And entry <> ID_SELF ThenSet mainDictionary(entry) = GetYAMLAsDictionary(mainDictionary(entry))End IfNext entryElseIf mainDictionary(ID_TYPE) = "Array" ThenDim i As IntegerDim subArray() As ObjectFor i = LBound(mainDictionary(ID_SELF)) To UBound(mainDictionary(ID_SELF))Debug.Print "=== PROCESSING ARRAY ENTRY ==="Debug.Print mainDictionary(ID_SELF)(i)'Set subDictionary = GetYAMLAsDictionary(mainDictionary(ID_SELF)(i))'Set mainDictionary(ID_SELF)(i) = subDictionaryReDim Preserve subArray(i)Set subArray(i) = GetYAMLAsDictionary(CStr(mainDictionary(ID_SELF)(i)))Next imainDictionary(ID_SELF) = subArrayElseIf mainDictionary(ID_TYPE) <> "String" ThenDebug.Print MESSAGE_MALFORMED_TYPE ', vbOKOnly, errIdentifier)End IfSet GetYAMLAsDictionary = mainDictionaryEnd Function' YAML Cleaner Pseudocode' =====' function YAMLCleaner(Dictionary mainDictionary) {' for each entry in mainDictionary {' if entry(ID_TYPE) == "Dictionary" {' for each secondOrderEntry in entry {' YAMLCleaner(secondOrderEntry)' }' } else if entry(ID_TYPE) == "Array" {' for each secondOrderEntry in entry(ID_SELF) {' YAMLCleaner(secondOrderEntry)' }' }' if entry(ID_TYPE) != "Dictionary" {' mainDictionary(entry) = mainDictionary(entry)(ID_SELF)' }' }' return mainDictionary;' }'Function YAMLCleaner(mainDictionary As Dictionary) As Dictionary' Dim entry As Variant' If mainDictionary(ID_TYPE) = "Array" Then ' go through array and yamlclean it' Dim i As Integer' Debug.Print JsonConverter.ConvertToJson(mainDictionary)' For i = LBound(mainDictionary(ID_SELF)) To UBound(mainDictionary(ID_SELF))'' 'If IsObject(mainDictionary(ID_SELF)(i)) Then' 'Set mainDictionary(ID_SELF)(i) = YAMLCleaner(mainDictionary(ID_SELF)(i))' 'Else' ' Debug.Print "encountered non-object"' 'End If' Next i' End If' If mainDictionary(ID_TYPE) = "Dictionary" Then 'iterate through dict and yamlclena it' For Each entry In mainDictionary' If entry <> ID_TYPE Then' Set mainDictionary(entry) = YAMLCleaner(mainDictionary(entry))' End If' Next entry' End If'' For Each entry In mainDictionary' If mainDictionary(ID_TYPE) = "Dictionary" And mainDictionary(entry)(ID_TYPE) <> "Dictionary" And entry <> ID_TYPE And entry <> ID_SELF Then' Debug.Print "processing " & entry & " which is " & mainDictionary(entry)(ID_TYPE)' If IsObject(mainDictionary(entry)(ID_SELF)) Then' Set mainDictionary(entry) = mainDictionary(entry)(ID_SELF)' Else' mainDictionary(entry) = mainDictionary(entry)(ID_SELF)' End If' End If' Next entry'' ' destroy type identifier?' Set YAMLCleaner = mainDictionary'End FunctionPrivate Function GetFileAsString(filePath As String) As String' Dim fileContent As StringDim line As StringDim fileNumber As Integer'filePath = "\\Mac\iCloud\Development\cv\cv.yml"fileNumber = FreeFile()Open filePath For Input As fileNumberDo While Not EOF(fileNumber)Line Input #fileNumber, lineGetFileAsString = GetFileAsString & line & vbCrLfLoopEnd FunctionPublic Property Let path(thePath As String)yamlPath = thePathEnd PropertyPublic Property Get path() As Stringpath = yamlPathEnd PropertyPublic Property Get props() As Scripting.DictionarySet props = GetYAMLAsDictionary(GetFileAsString(yamlPath))End Property' YAML Indexer Pseudocode [implement later]' =====' function index(string theIndex) {' Variant[] mainArray = theIndex.split(".");' Dictionary mainDictionary = yamlProps;' for each entry in mainArray {' if entry is {'' ....