#!/usr/bin/env python3 import re import json import hashlib from enum import Enum, auto from .nag_object import NagObject, NagObjectType class ParserState(Enum): COMMENT = auto() DEFINE = auto() DIRECTIVE = auto() MULTILINE = auto() def hash_string(string:str) -> int: hash = hashlib.sha256() hash.update(string.encode('utf-8')) digest = hash.hexdigest() return int(digest,16) * 1000000 class NagParse: def __init__(self): self.m_config_table: dict[int,str] = dict() self.m_objects: list[NagObject] = list() def __str__(self): string = str() for object in self.m_objects: string += str(object) + "\n" return string def consume_config_files(self, config_files: list[str]) -> None: for config_file in config_files: self.m_config_table[hash_string(config_file)] = config_file for object in self.parse_config(config_file): self.m_objects.append(object) def consume_config_list(self, config_list: list[dict] ) -> None: for object in config_list: nag_object = NagObject() nag_object.from_object(object) self.m_objects.append(nag_object) def to_json(self): return json.dumps([x.to_object() for x in self.m_objects]) def parse_config(self,config_file: str) -> list[NagObject]: state = ParserState.DEFINE objects: list[NagObject] = list() with open(config_file, 'r') as file_handle: line_no = 0 cur_object = NagObject(config_file=config_file) for rawline in file_handle.readlines(): # Clean up the config line = re.sub(r'([a-z]){', r'\1 {',rawline.strip()) #line = re.sub(r'([a-zA-Z0-9])}', r'\1\n}',line.strip()) #line = re.sub(r'([a-zA-Z0-9]) }', r'\1\n}',line.strip()) line_no += 1 # This means it is a comment, we might want to save those later on if len(line) == 0 or re.match('^[\t ]*[#]+', line): continue # This means we are done parsing and should reset for new object if re.match(r'}', line): objects.append(cur_object) del cur_object state = ParserState.DEFINE continue #if we get here it is not a comment and not the end of an object fragments: list[str] = re.sub(' +', ' ', line.strip()).split() # This is a new object and we should see what type it is, and what line number it is on if state == ParserState.DEFINE: if fragments[0] == 'define': cur_object = NagObject(config_file=config_file, directives=list()) cur_object.m_line_no = line_no cur_object.m_type = NagObjectType(fragments[1]) # For the next line we should be in DIRECTIVE state state = ParserState.DIRECTIVE else: print("It's fucked up man") break # This is the DIRECTIVE state, where we want to collect key, values and find out if we should go to multiline mode elif state == ParserState.DIRECTIVE: if fragments[-1][-1] == '\\': state = ParserState.MULTILINE if re.match('^[a-z]+', fragments[0]): cur_object.m_directives.append( [fragments[0], " ".join(fragments[1:])]) # This is the MULTILINE state, directives can be on many lines if the '\n' is escaped with '\' elif state == ParserState.MULTILINE: cur_object.m_directives[-1][1] = cur_object.m_directives[-1][1].strip('\\') + ' ' + " ".join(fragments) if fragments[-1][-1] != '\\': state = ParserState.DIRECTIVE else: print("It's fucked up man") break return objects