1#!/usr/bin/env python 2# Copyright 2014 by Sam Mikes. All rights reserved. 3# This code is governed by the BSD license found in the LICENSE file. 4 5# This code provides a fallback parser that can handle the subset of 6# YAML used in test262 frontmatter 7 8import re 9 10mYamlKV = re.compile(r"(.*?):(.*)") 11mYamlIntDigits = re.compile(r"^[-0-9]*$") 12mYamlFloatDigits = re.compile(r"^[-.0-9eE]*$") 13mYamlListPattern = re.compile(r"^\[(.*)\]$") 14mYamlMultilineList = re.compile(r"^ *- (.*)$") 15mYamlStringValue = re.compile(r"^('|\").*\1$") 16 17def load(str): 18 return myReadDict(str.splitlines())[1] 19 20def myReadDict(lines, indent=""): 21 dict = None 22 key = None 23 emptyLines = 0 24 25 while lines: 26 if not lines[0].startswith(indent): 27 break 28 29 line = lines.pop(0) 30 if myIsAllSpaces(line): 31 emptyLines += 1 32 continue 33 result = mYamlKV.match(line) 34 35 if result: 36 if not dict: 37 dict = {} 38 key = result.group(1).strip() 39 value = result.group(2).strip() 40 (lines, value) = myReadValue(lines, value, indent) 41 dict[key] = value 42 else: 43 if dict and key and key in dict: 44 c = " " if emptyLines == 0 else "\n" * emptyLines 45 dict[key] += c + line.strip() 46 else: 47 raise Exception("monkeyYaml is confused at " + line) 48 emptyLines = 0 49 return lines, dict 50 51def myReadValue(lines, value, indent): 52 if value == ">" or value == "|": 53 (lines, value) = myMultiline(lines, value == "|") 54 value = value + "\n" 55 return (lines, value) 56 if lines and not value: 57 if myMaybeList(lines[0]): 58 return myMultilineList(lines, value) 59 indentMatch = re.match("(" + indent + r"\s+)", lines[0]) 60 if indentMatch: 61 if ":" in lines[0]: 62 return myReadDict(lines, indentMatch.group(1)) 63 return myMultiline(lines, False) 64 return lines, myReadOneLine(value) 65 66def myMaybeList(value): 67 return mYamlMultilineList.match(value) 68 69def myMultilineList(lines, value): 70 # assume no explcit indentor (otherwise have to parse value) 71 value = [] 72 indent = 0 73 while lines: 74 line = lines.pop(0) 75 leading = myLeadingSpaces(line) 76 if myIsAllSpaces(line): 77 pass 78 elif leading < indent: 79 lines.insert(0, line) 80 break; 81 else: 82 indent = indent or leading 83 value += [myReadOneLine(myRemoveListHeader(indent, line))] 84 return (lines, value) 85 86def myRemoveListHeader(indent, line): 87 line = line[indent:] 88 return mYamlMultilineList.match(line).group(1) 89 90def myReadOneLine(value): 91 if mYamlListPattern.match(value): 92 return myFlowList(value) 93 elif mYamlIntDigits.match(value): 94 try: 95 value = int(value) 96 except ValueError: 97 pass 98 elif mYamlFloatDigits.match(value): 99 try: 100 value = float(value) 101 except ValueError: 102 pass 103 elif mYamlStringValue.match(value): 104 value = value[1:-1] 105 return value 106 107def myFlowList(value): 108 result = mYamlListPattern.match(value) 109 values = result.group(1).split(",") 110 return [myReadOneLine(v.strip()) for v in values] 111 112def myMultiline(lines, preserveNewlines=False): 113 # assume no explcit indentor (otherwise have to parse value) 114 value = "" 115 indent = myLeadingSpaces(lines[0]) 116 wasEmpty = None 117 118 while lines: 119 line = lines.pop(0) 120 isEmpty = myIsAllSpaces(line) 121 122 if isEmpty: 123 if preserveNewlines: 124 value += "\n" 125 elif myLeadingSpaces(line) < indent: 126 lines.insert(0, line) 127 break; 128 else: 129 if preserveNewlines: 130 if wasEmpty != None: 131 value += "\n" 132 else: 133 if wasEmpty == False: 134 value += " " 135 elif wasEmpty == True: 136 value += "\n" 137 value += line[(indent):] 138 139 wasEmpty = isEmpty 140 141 return (lines, value) 142 143def myIsAllSpaces(line): 144 return len(line.strip()) == 0 145 146def myLeadingSpaces(line): 147 return len(line) - len(line.lstrip(' ')) 148