diff options
Diffstat (limited to 'python/qpid/address.py')
-rw-r--r-- | python/qpid/address.py | 127 |
1 files changed, 94 insertions, 33 deletions
diff --git a/python/qpid/address.py b/python/qpid/address.py index 3355d1a591..6ad6f23255 100644 --- a/python/qpid/address.py +++ b/python/qpid/address.py @@ -34,22 +34,33 @@ class Type: LBRACE = Type("LBRACE", r"\{") RBRACE = Type("RBRACE", r"\}") COLON = Type("COLON", r":") -COMMA = Type("COMMA", r",") +SEMI = Type("SEMI", r";") SLASH = Type("SLASH", r"/") -ID = Type("ID", r'[a-zA-Z_][a-zA-Z0-9_.#*-]*') +COMMA = Type("COMMA", r",") NUMBER = Type("NUMBER", r'[+-]?[0-9]*\.?[0-9]+') +ID = Type("ID", r'[a-zA-Z_][a-zA-Z0-9_]*') STRING = Type("STRING", r""""(?:[^\\"]|\\.)*"|'(?:[^\\']|\\.)*'""") +ESC = Type("ESC", r"\\[^ux]|\\x[0-9][0-9]|\\u[0-9][0-9][0-9][0-0]") +SYM = Type("SYM", r"[.#*%@$^!+-]") WSPACE = Type("WSPACE", r"[ \n\r\t]+") EOF = Type("EOF") class Token: - def __init__(self, type, value): + def __init__(self, type, value, input, position): self.type = type self.value = value + self.input = input + self.position = position + + def line_info(self): + return line_info(self.input, self.position) def __repr__(self): - return "%s: %r" % (self.type, self.value) + if self.value is None: + return repr(self.type) + else: + return "%s(%r)" % (self.type, self.value) joined = "|".join(["(%s)" % t.pattern for t in TYPES]) LEXER = re.compile(joined) @@ -83,15 +94,51 @@ def lex(st): m = LEXER.match(st, pos) if m is None: line, ln, col = line_info(st, pos) - raise LexError("unrecognized character in <string>:%s,%s: %s" % (ln, col, line)) + raise LexError("unrecognized characters line:%s,%s: %s" % (ln, col, line)) else: idx = m.lastindex - t = Token(TYPES[idx - 1], m.group(idx)) + t = Token(TYPES[idx - 1], m.group(idx), st, pos) yield t pos = m.end() - yield Token(EOF, None) - -class ParseError(Exception): pass + yield Token(EOF, None, st, pos) + +def tok2str(tok): + if tok.type is STRING: + return eval(tok.value) + elif tok.type is ESC: + if tok.value[1] in ("x", "u"): + return eval('"%s"' % tok.value) + else: + return tok.value[1] + else: + return tok.value + +def tok2obj(tok): + if tok.type in (STRING, NUMBER): + return eval(tok.value) + else: + return tok.value + +def toks2str(toks): + if toks: + return "".join(map(tok2str, toks)) + else: + return None + +class ParseError(Exception): + + def __init__(self, token, *expected): + line, ln, col = token.line_info() + exp = ", ".join(map(str, expected)) + if len(expected) > 1: + exp = "(%s)" % exp + if expected: + msg = "expecting %s, got %s line:%s,%s:%s" % (exp, token, ln, col, line) + else: + msg = "unexpected token %s line:%s,%s:%s" % (token, ln, col, line) + Exception.__init__(self, msg) + self.token = token + self.expected = expected class Parser: @@ -107,46 +154,62 @@ class Parser: def eat(self, *types): if types and not self.matches(*types): - raise ParseError("expecting %s -- got %s" % (", ".join(map(str, types)), self.next())) + raise ParseError(self.next(), *types) else: t = self.next() self.idx += 1 return t + def eat_until(self, *types): + result = [] + while not self.matches(*types): + result.append(self.eat()) + return result + def parse(self): result = self.address() self.eat(EOF) return result def address(self): - name = self.eat(ID).value - subject = None - options = None + name = toks2str(self.eat_until(SLASH, SEMI, EOF)) + + if name is None: + raise ParseError(self.next()) + if self.matches(SLASH): self.eat(SLASH) - if self.matches(ID): - subject = self.eat(ID).value - else: - subject = "" - elif self.matches(LBRACE): + subject = toks2str(self.eat_until(SEMI, EOF)) + else: + subject = None + + if self.matches(SEMI): + self.eat(SEMI) options = self.map() + else: + options = None return name, subject, options def map(self): self.eat(LBRACE) + result = {} while True: - if self.matches(RBRACE): - self.eat(RBRACE) - break - else: - if self.matches(ID): - n, v = self.nameval() - result[n] = v - elif self.matches(COMMA): + if self.matches(ID): + n, v = self.nameval() + result[n] = v + if self.matches(COMMA): self.eat(COMMA) + elif self.matches(RBRACE): + break else: - raise ParseError("expecting (ID, COMMA), got %s" % self.next()) + raise ParseError(self.next(), COMMA, RBRACE) + elif self.matches(RBRACE): + break + else: + raise ParseError(self.next(), ID, RBRACE) + + self.eat(RBRACE) return result def nameval(self): @@ -156,16 +219,14 @@ class Parser: return (name, val) def value(self): - if self.matches(NUMBER, STRING): - return eval(self.eat().value) - elif self.matches(ID): - return self.eat().value + if self.matches(NUMBER, STRING, ID): + return tok2obj(self.eat()) elif self.matches(LBRACE): return self.map() else: - raise ParseError("expecting (NUMBER, STRING, LBRACE) got %s" % self.next()) + raise ParseError(self.next(), NUMBER, STRING, ID, LBRACE) def parse(addr): return Parser(lex(addr)).parse() -__all__ = ["parse"] +__all__ = ["parse", "ParseError"] |