| 1 | """ |
|---|
| 2 | Basic parsing of arguments into a non-keyword list and keyword dictionary. |
|---|
| 3 | """ |
|---|
| 4 | |
|---|
| 5 | import re |
|---|
| 6 | |
|---|
| 7 | class UnexpectedEndOfInput(Exception): pass |
|---|
| 8 | class InvalidToken(Exception): pass |
|---|
| 9 | class UnexpectedToken(Exception): pass |
|---|
| 10 | |
|---|
| 11 | class Lexer(object): |
|---|
| 12 | """ Convenience wrapper around tokenize.generate_tokens """ |
|---|
| 13 | _lexer = re.compile(r'''([][(),=]|"[^"]*"|'[^']*'|[^][(),=\s]+)''') |
|---|
| 14 | |
|---|
| 15 | def __init__(self, string): |
|---|
| 16 | tokens = self._lexer.findall(string) |
|---|
| 17 | self.tokens = [] |
|---|
| 18 | for token in tokens: |
|---|
| 19 | type = 1 |
|---|
| 20 | if token[0] in '\'"': |
|---|
| 21 | type = 3 |
|---|
| 22 | self.tokens.append((type, token)) |
|---|
| 23 | |
|---|
| 24 | def __iter__(self): |
|---|
| 25 | return self |
|---|
| 26 | |
|---|
| 27 | def next(self): |
|---|
| 28 | if self.tokens: |
|---|
| 29 | token = self.tokens.pop(0) |
|---|
| 30 | return token |
|---|
| 31 | raise StopIteration |
|---|
| 32 | |
|---|
| 33 | def push_token(self, token): |
|---|
| 34 | self.tokens.insert(0, token) |
|---|
| 35 | |
|---|
| 36 | def parseargs(arguments): |
|---|
| 37 | """ Parse a python-like set of arguments. """ |
|---|
| 38 | def parse_dict(lexer): |
|---|
| 39 | out = {} |
|---|
| 40 | try: |
|---|
| 41 | for type, token in lexer: |
|---|
| 42 | if token == '}': |
|---|
| 43 | return out |
|---|
| 44 | lexer.push_token((type, token)) |
|---|
| 45 | key = parse_value(lexer) |
|---|
| 46 | type, token = lexer.get_token() |
|---|
| 47 | if token[1] not in '=:': |
|---|
| 48 | raise UnexpectedToken(token) |
|---|
| 49 | value = parse_node(lexer) |
|---|
| 50 | except StopIteration: |
|---|
| 51 | pass |
|---|
| 52 | raise UnexpectedEndOfInput |
|---|
| 53 | |
|---|
| 54 | def parse_array(lexer): |
|---|
| 55 | out = [] |
|---|
| 56 | try: |
|---|
| 57 | for type, token in lexer: |
|---|
| 58 | if out and token == ',': |
|---|
| 59 | type, token = lexer.next() |
|---|
| 60 | if token in '])': |
|---|
| 61 | return out |
|---|
| 62 | lexer.push_token((type, token)) |
|---|
| 63 | out.append(parse_node(lexer)) |
|---|
| 64 | except StopIteration: |
|---|
| 65 | pass |
|---|
| 66 | raise UnexpectedEndOfInput |
|---|
| 67 | |
|---|
| 68 | def parse_value(lexer): |
|---|
| 69 | type, token = lexer.next() |
|---|
| 70 | if type == 2: |
|---|
| 71 | return float(token) |
|---|
| 72 | elif type == 3: |
|---|
| 73 | return token[1:-1] |
|---|
| 74 | elif type in (1, 51): |
|---|
| 75 | return token |
|---|
| 76 | else: |
|---|
| 77 | raise InvalidToken(token) |
|---|
| 78 | |
|---|
| 79 | def parse_node(lexer): |
|---|
| 80 | type, token = lexer.next() |
|---|
| 81 | if token in '([': |
|---|
| 82 | return parse_array(lexer) |
|---|
| 83 | elif token == '{': |
|---|
| 84 | return parse_dict(lexer) |
|---|
| 85 | else: |
|---|
| 86 | lexer.push_token((type, token)) |
|---|
| 87 | return parse_value(lexer) |
|---|
| 88 | |
|---|
| 89 | lexer = Lexer(arguments) |
|---|
| 90 | args = [] |
|---|
| 91 | kwargs = {} |
|---|
| 92 | |
|---|
| 93 | try: |
|---|
| 94 | while True: |
|---|
| 95 | arg = parse_value(lexer) |
|---|
| 96 | try: |
|---|
| 97 | type, token = lexer.next() |
|---|
| 98 | except StopIteration: |
|---|
| 99 | args.append(arg) |
|---|
| 100 | break |
|---|
| 101 | if token == '=': |
|---|
| 102 | kwargs[str(arg)] = parse_node(lexer) |
|---|
| 103 | type, token = lexer.next() |
|---|
| 104 | if token != ',': |
|---|
| 105 | raise UnexpectedToken(token) |
|---|
| 106 | elif token == ',': |
|---|
| 107 | args.append(arg) |
|---|
| 108 | except StopIteration: |
|---|
| 109 | pass |
|---|
| 110 | return args, kwargs |
|---|
| 111 | |
|---|
| 112 | if __name__ == '__main__': |
|---|
| 113 | print parseargs('foo, bar') |
|---|