summaryrefslogtreecommitdiff
path: root/python/qpid/address.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/qpid/address.py')
-rw-r--r--python/qpid/address.py127
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"]