对参数解析这一块挺感兴趣,想用之前了解到的知识来处理命令行参数,想到了解释器的工作原理,于是便简单实现了一个以解释的思想来处理命令行参数的方法。很早就想学习编译原理相关的知识不过一直没有付诸于实践,导致无法清晰的理顺命令及参数之间的逻辑,ParameterInterpreter处理参数的逻辑没有想好。
# -*- coding:utf-8 -*- EOF, COMMAND, PARAMETER = "EOF", "COMMAND", "PARAMETER" import sys class Token(object): def __init__(self, type, value): self.type = type self.value = value def __str__(self): return 'Token({type}, {value})'.format( type=self.type, value=repr(self.value) ) def __repr__(self): return self.__str__() class Lexer(object): def __init__(self, text): self.text = text self.pos = 0 self.current_char = self.text[self.pos] if len(text) > 1 else None def error(self): raise Exception("Error parsing input") def advance(self): self.pos += 1 if self.pos > len(self.text) - 1: self.current_char = None # Indicates end of input else: self.current_char = self.text[self.pos] def skip_whitespace(self): while self.current_char is not None and self.current_char.isspace(): self.advance() def check_whitespace(self): if self.current_char is not None and self.current_char.isspace(): self.advance() elif self.current_char is None: pass else: raise Exception("Must separate commands with whitespace") # assume command is always two char form and have to be separated by space at the end of command def command(self): result = "" result += self.current_char self.advance() if self.current_char is None: self.error() result += self.current_char self.advance() self.check_whitespace() return result def parameter(self): result = "" while self.current_char is not None and ( self.current_char.isalpha() or self.current_char.isdigit() or self.current_char == "."): result += self.current_char self.advance() return result def get_next_token(self): while self.current_char is not None: if self.current_char.isspace(): self.skip_whitespace() if self.current_char == "-": return Token(COMMAND, self.command()) if self.current_char.isalpha() or self.current_char.isdigit(): return Token(PARAMETER, self.parameter()) self.error() return Token(EOF, None) class ParameterInterpreter(object): def __init__(self, lexer): self.lexer = lexer self.current_token = self.lexer.get_next_token() self.commands = {} # p:parameter h:help r:required rp:required_parameter t:triggered def put_command(self, command, required=False, require_parameter=False, _help=None): """ customize commands""" if not self.commands.get(command, None): self.commands[command] = {"p": None, "h": _help, "r": required, "rp": require_parameter, "t": False} else: raise Exception("command already exists!") def error(self): raise Exception('Invalid syntax') def eat(self, type): if self.current_token.type == type: self.current_token = self.lexer.get_next_token() else: self.error() def base(self): """COMMAND""" # to handle the prefix of a command token = self.current_token if token.value in self.commands.keys(): self.eat(COMMAND) self.commands[token.value]["t"] = True else: raise Exception("{} command not defined".format(token.value)) return token def combination(self): """COMMAND ARGUMENT""" command_token = self.base() token = self.current_token if self.commands[command_token.value]["rp"] and token.type == PARAMETER: self.eat(PARAMETER) self.commands[command_token.value]["p"] = token.value elif not self.commands[command_token.value]["rp"] and token.type == PARAMETER: raise Exception("{} does not take a parameter".format(command_token.value)) elif not self.commands[command_token.value]["rp"]: return else: raise Exception("{} requires a parameter".format(command_token.value)) def handle_argument(self): """(COMMAND ARGUMENT)* :return result dictionary """ while self.current_token.type == COMMAND: self.combination() if self.current_token.type != EOF: self.error() return self.commands def main(): try: args = sys.argv text = " ".join(args[1:]) except: EOFError() text = "" l = Lexer(text) pi = ParameterInterpreter(l) pi.put_command("-s", require_parameter=True, required=True) pi.put_command("-l", require_parameter=False) pi.put_command("-w", require_parameter=False) pi.put_command("-a", require_parameter=False) result = pi.handle_argument() print(result) if __name__ == '__main__': main()
测试结果:
现在正在将该argparser用C语言重写,打算学习了编译原理过后将代码重构一下,顺着这个思路写一个argparser的工具模块。