import sys class TokenException(Exception): pass class ParseException(Exception): pass class Tokenizer: def __init__(self, f): """ Init the tokenizer on the given file, may throw an exception. """ self.i = 0 # Read the file, coud be streamed as well self.f = f self.data = file(f).read() self.state = "linestart" def get_next_token(self): """ Returns the next token in the file. A token is one of: < = > name Returns None when the end of file is reached and beyond """ while self.i < len(self.data): char = self.data[self.i] # Skip leading spaces (which are really trailing spaces after a token # Newline is important so special case it if char.isspace() and not char == "\n": self.i += 1 continue if self.state == "linestart" and char == "#": # Eat all the comment line while self.i < len(self.data) and self.data[self.i] != "\n": self.i += 1 self.i += 1 #position on first char after newline continue # At this point, we can only have a token, read it, and return it # The method updates the self.i to point to the new char to read next next_token = self.read_token() # Wait ! We got a newline here, so back to start again.. if next_token == "\n": self.state = "linestart" continue return next_token def read_token(self): """ Return the token, self.i must point to the first char in this token. self.i is updated to point just after the returned token Returned token is one of: < = > \n name """ char = self.data[self.i] if char == "<" or char == "=" or char == ">" or char == "\n": self.i += 1 return char elif char == "\"": # Here we assume proper quoting closing = self.data[self.i+1:].find("\"") if closing == -1: raise TokenException("Couldn't find a proper closing quote in %s" % self.f) token = self.data[self.i+1:self.i+1+closing] self.i += closing+2 #Next char is just *after* the closing " return token else: # Token can just be a string now.. token = "" # We eat all chars until one of "=<>" or whitespace is found while self.i < len(self.data) and not self.data[self.i].isspace() and not self.data[self.i] in "=><": token += self.data[self.i] self.i += 1 return token class MozillaParser: def __init__(self, f): """ Init the parse, no exception here """ self.f = f self.infos = { "search": {}, "input": {}, } def parse(self): """ """ tokenizer = Tokenizer(self.f) n = state = None # We load the two first tokens, whch should be ["<", "search"] tokens = [tokenizer.get_next_token(), tokenizer.get_next_token()] while tokens[0] != None: # Retreive the next state, the number of tokens to read next, and any discarded tokens # by the handler, which should be handled on next iteration state, n, rest = self._handle_token(state, tokens) # If n == None, we finished parsing if n == None: break # Read the requested number of tokens, but count the ones that were rejected first tokens = rest for i in range(n - len(rest)): tokens.append(tokenizer.get_next_token()) def _handle_token(self, state, tokens): if state == None and (tokens == ["<", "search"] or tokens == ["<", "SEARCH"]): return "search", 3, [] elif state == None: raise ParseException("File %s does not begin with tags if state == "search" or state == "anotherinput" and tokens == [">", "<", "input"]: return "input", 6, [] elif state == "search": raise ParseException("Expecting