Simple Moe configuration file syntax parser.
-TODO: decide '()' around formulas
+TODO: decide neccessity of '()' in/around formulas
TODO: check escaping in expressions
TODO: should whitespace (incl. '\\n') be allowed (almost) everywhere?
can comment be anywhere whitespace can?
VARNAME = re('[a-zA-Z0-9-_]+(\.[a-zA-Z0-9-_]+)*')
"""
-import re, logging as log
+import re, itertools, logging as log
-class ConfSyntaxError(Exception):
- # TODO: choose better superclass
+class ConfigSyntaxError(Exception):
+ # TODO: choose a better superclass
def __init__(self, msg, fname='<unknown>', line=None, column=None):
self.msg = msg
self.fname = fname
self.line = line
self.column = column
def __str__(self):
- return('ConfSyntaxError %s:%d:%d: %s'%(self.fname, self.line, self.column, self.msg))
-
-c_varname_sep = u'.'
-c_comment = u'#'
-c_open = u'{'
-c_close = u'}'
-c_ws = u' \t\n'
-c_sep = u';\n'
-c_nl = u'\n'
-c_if = u'if'
-c_and = u'and'
-c_or = u'or'
-c_not = u'not'
-c_eq = u'=='
-c_neq = u'!='
-c_set = u'='
-c_append = u'+='
+ return('ConfigSyntaxError %s:%d:%d: %s'%(self.fname, self.line, self.column, self.msg))
"Variable name regexp, dots (separators) must be separated from edges and each other."
re_VARNAME = re.compile(r'\A([A-Za-z0-9_-]+\.)*[A-Za-z0-9_-]+\Z')
-class ConfParser(object):
+class ConfigParser(object):
+ c_varname_sep = u'.'
+ c_comment = u'#'
+ c_open = u'{'
+ c_close = u'}'
+ c_ws = u' \t\n'
+ c_sep = u';\n'
+ c_nl = u'\n'
+ c_if = u'if'
+ c_and = u'and'
+ c_or = u'or'
+ c_not = u'not'
+ c_eq = u'=='
+ c_neq = u'!='
+ c_set = u'='
+ c_append = u'+='
def __init__(self, f, tree, fname='<unknown>'):
self.f = f # Stream
self.fname = fname # Filename
p_BLOCK(self)
def p_BLOCK(self):
self.p_WS()
- while not self.eof() and not f.peek(c_close):
+ while not self.eof() and not f.peek(self.c_close):
self.p_STATEMENT()
slef.p_WS()
- if not self.peek() in c_sep:
+ if not self.peek() in self.c_sep:
break
self.p_SEP()
self.p_WS()
def p_WS():
while not self.eof():
- if self.peek() in c_ws:
+ if self.peek() in self.c_ws:
self.next()
- elif self.peeks(c_comment):
+ elif self.peeks(self.c_comment):
self.p_COMMENT()
else:
break
def p_COMMENT(self):
- self.expect(c_comment, "'#' expected at the beginning of a comment.")
- while not self.eof() and not self.nexts(c_nl):
+ self.expect(self.c_comment, "'#' expected at the beginning of a comment.")
+ while not self.eof() and not self.nexts(self.c_nl):
pass
- self.eof() or self.expect(c_nl)
+ self.eof() or self.expect(self.c_nl)
def p_STATEMENT(self):
self.p_WS()
- if self.peeks(c_if):
+ if self.peeks(self.c_if):
self.p_CONDITION()
else:
# for operation or subtree, read VARNAME
varname = self.p_VARNAME()
self.p_WS()
- if self.nexts(c_open):
+ if self.nexts(self.c_open):
self.p_BLOCK(varname)
self.p_WS()
- self.expect(c_close)
+ self.expect(self.c_close)
else:
self.p_OPERATION(varname)
def p_SUBTREE(self, varname=None):
self.p_WS()
varname = self.p_VARNAME()
self.p_WS()
- self.expect(c_open)
+ self.expect(self.c_open)
# backup and extend the variable name prefix
p = self.prefix
- self.prefix = p + c_varname_sep + varname
+ self.prefix = p + self.c_varname_sep + varname
self.p_BLOCK()
self.prefix = p
# close block and
self.p_WS()
- self.expect(c_close)
+ self.expect(self.c_close)
def p_OPERATION(self, varname=None):
if not varname:
self.p_WS()
varname = self.p_VARNAME()
self.p_WS()
- if self.nexts(c_set):
+ if self.nexts(self.c_set):
op = 'SET'
- elif self.nexts(c_append):
+ elif self.nexts(self.c_append):
op = 'APPEND'
else:
self.syntaxError('Unknown operation.')
self.p_WS()
exp = self.p_EXPRESSION()
- v = self.tree.lookup((self.prefix+c_varname_sep+varname).lstrip(c_varname_sep))
+ v = self.tree.lookup((self.prefix+self.c_varname_sep+varname).lstrip(self.c_varname_sep))
if self.conditions:
cnd = self.conditions[-1]
else:
v.add_operation(op, cnd, exp, self.priority)
def p_CONDITION(self):
self.p_WS()
- self.expect(c_if)
+ self.expect(self.c_if)
self.p_WS()
f = p_FORMULA(self)
cnd = ConfigCondition(f)
self.conditions.append(cnd)
# Parse a block
self.p_WS()
- self.expect(c_open)
+ self.expect(self.c_open)
self.p_BLOCK()
self.p_WS()
- self.expect(c_close)
+ self.expect(self.c_close)
# Cleanup
self.conditions.pop()
def p_VARNAME(self):
if self.nexts(u'\\'):
# Escape sequence
c = self.next()
- if c not in u'\\"n' + c_open + c_close:
+ if c not in u'\\"n' + self.c_open + self.c_close:
self.syntax_error('Illeal escape sequence in expression')
if c == 'n':
expr.append(u'\n')
else:
expr.append(c)
exl.append(c)
- elif self.nexts(c_open):
+ elif self.nexts(self.c_open):
# Parse a variable name in '{}'
varname = self.p_VARNAME()
- self.expect(c_close)
+ self.expect(self.c_close)
exl.append(varname)
expr.append(self.tree.lookup(varname))
else:
if self.nexts(u'('):
f1 = self.p_FORMULA()
self.p_WS()
- if self.nexts(c_and):
+ if self.nexts(self.c_and):
f2 = self.p_FORMULA()
self.p_WS()
self.expect(u')')
return ('AND', f1, f2)
- elif self.nexts(c_or):
+ elif self.nexts(self.c_or):
f2 = self.p_FORMULA()
self.p_WS()
self.expect(u')')
return f1
else:
self.syntax_error("Logic operator or ')' expected")
- elif self.nexts(c_not):
+ elif self.nexts(self.c_not):
# 'not' formula
f = self.p_FORMULA()
return ('NOT', f)
# Should be (in)equality condition
e1 = self.p_EXPRESSION()
self.p_WS()
- if self.nexts(c_eq):
+ if self.nexts(self.c_eq):
self.p_WS()
e2 = self.p_EXPRESSION()
return ('==', e1, e2)
- elif self.nexts(c_neq):
+ elif self.nexts(self.c_neq):
self.p_WS()
e2 = self.p_EXPRESSION()
return ('!=', e1, e2)