def parse(file_contents, filename='') -> "List of Property objects": """Returns list of Property objects parsed from given text""" open_properties = [Property(None, [])] for line_num, line in enumerate(file_contents, start=1): values = open_properties[-1].value freshline = utils.clean_line(line) if not freshline: # Skip blank lines! continue if freshline.startswith('"'): # data string line_contents = freshline.split('"') name = line_contents[1] if not utils.is_identifier(name): raise KeyValError( 'Invalid name ' + name + '!', filename, line_num, ) try: value = line_contents[3] if not freshline.endswith('"'): raise KeyValError( 'Key has value, but incomplete quotes!', filename, line_num, ) for orig, new in REPLACE_CHARS.items(): value = value.replace(orig, new) except IndexError: value = None values.append(Property(name, value)) # handle name bare on one line, will need a brace on # the next line elif utils.is_identifier(freshline): values.append(Property(freshline, [])) elif freshline.startswith('{'): if values[-1].value: raise KeyValError( 'Property cannot have sub-section if it already' 'has an in-line value.', filename, line_num, ) values[-1].value = [] open_properties.append(values[-1]) elif freshline.startswith('}'): open_properties.pop() else: raise KeyValError( "Unexpected beginning character '" + freshline[0] + '"!', filename, line_num, ) if not open_properties: raise KeyValError( 'Too many closing brackets.', filename, line_num, ) if len(open_properties) > 1: raise KeyValError( 'End of text reached with remaining open sections.', filename, line=None, ) return open_properties[0]
def parse(file_contents, filename='') -> "Property": """Returns a Property tree parsed from given text. filename, if set should be the source of the text for debug purposes. file_contents should be an iterable of strings """ from utils import is_identifier file_iter = enumerate(file_contents, start=1) # The block we are currently adding to. # The special name 'None' marks it as the root property, which # just outputs its children when exported. This way we can handle # multiple root blocks in the file, while still returning a single # Property object which has all the methods. cur_block = Property(None, []) # A queue of the properties we are currently in (outside to inside). open_properties = [cur_block] for line_num, line in file_iter: if isinstance(line, bytes): # Decode bytes using utf-8 line = line.decode('utf-8') freshline = line.strip() if not freshline or freshline[:2] == '//': # Skip blank lines and comments! continue if freshline.startswith('"'): # data string line_contents = freshline.split('"') name = line_contents[1] try: value = line_contents[3] except IndexError: # It doesn't have a value, likely a block value = None else: # Special case - comment between name/value sections - # it's a name block then. if line_contents[2].lstrip().startswith('//'): value = None del line_contents[3:] else: if len(line_contents) < 5: # It's a multiline value - no ending quote! value += read_multiline_value( file_iter, line_num, filename, ) if value and '\\' in value: for orig, new in REPLACE_CHARS.items(): value = value.replace(orig, new) # Line_contents[4] is the start of the comment, ensure that it's # blank or starts with a comment. if len(line_contents) >= 5: comment = line_contents[4].lstrip() # same as: not (comment or comment.startswith('//')) if comment and not comment.startswith('//'): raise KeyValError( 'Extra text after ' 'line: "{}"'.format(line_contents[4]), filename, line_num, ) cur_block.append(Property(name, value)) elif freshline.startswith('{'): # Open a new block. # If we're expecting a block, the value will be None. if cur_block[-1].value is not None: raise KeyValError( 'Property cannot have sub-section if it already' 'has an in-line value.', filename, line_num, ) cur_block = cur_block[-1] cur_block.value = [] open_properties.append(cur_block) elif freshline.startswith('}'): # Move back a block open_properties.pop() cur_block = open_properties[-1].value # handle name bare on one line, will need a brace on # the next line elif is_identifier(freshline): cur_block.append(Property(freshline, None)) else: raise KeyValError( "Unexpected beginning character '" + freshline[0] + '"!', filename, line_num, ) if not open_properties: raise KeyValError( 'Too many closing brackets.', filename, line_num, ) if len(open_properties) > 1: raise KeyValError( 'End of text reached with remaining open sections.', filename, line=None, ) return open_properties[0]
def parse(file_contents, filename='') -> "Property": """Returns a Property tree parsed from given text. filename, if set should be the source of the text for debug purposes. file_contents should be an iterable of strings """ open_properties = [Property(None, [])] for line_num, line in enumerate(file_contents, start=1): values = open_properties[-1].value freshline = utils.clean_line(line) if not freshline: # Skip blank lines! continue if freshline.startswith('"'): # data string line_contents = freshline.split('"') name = line_contents[1] if not utils.is_identifier(name): raise KeyValError( 'Invalid name ' + name + '!', filename, line_num, ) try: value = line_contents[3] if not freshline.endswith('"'): raise KeyValError( 'Key has value, but incomplete quotes!', filename, line_num, ) for orig, new in REPLACE_CHARS.items(): value = value.replace(orig, new) except IndexError: value = None values.append(Property(name, value)) elif freshline.startswith('{'): if values[-1].value: raise KeyValError( 'Property cannot have sub-section if it already' 'has an in-line value.', filename, line_num, ) values[-1].value = [] open_properties.append(values[-1]) elif freshline.startswith('}'): open_properties.pop() # handle name bare on one line, will need a brace on # the next line elif utils.is_identifier(freshline): values.append(Property(freshline, [])) else: raise KeyValError( "Unexpected beginning character '" + freshline[0] + '"!', filename, line_num, ) if not open_properties: raise KeyValError( 'Too many closing brackets.', filename, line_num, ) if len(open_properties) > 1: raise KeyValError( 'End of text reached with remaining open sections.', filename, line=None, ) return open_properties[0]