properties.py
'''\
Properties Classes.
classes:
Properties - Properties base class
ApplicationProperties - name=value pairs (values are all strings)
ObjectProperties - name=value pairs (values are tuples of tokens)
QueryProperties - name(value) specifications
Developer@Sonnack.com
February 2014
'''
from sys import stdout, stderr, argv
from lexer import SourceFile, Lexer, LexerLine
ListToString = lambda lst: reduce(lambda acc,s: acc+s, lst, '')
class Properties (object):
'''\
Properties base class.
properties:
.props - properties collection
.filename - properties file name
methods:
[N] - Nth argument
.len() - number of arguments
.str() - string representation
.repr() - JSON-like representation
'''
def keys_sorted_by_name (self):
'''All items sorted by name.'''
return sorted(self.props.keys(), key=lambda k: str(k).lower())
def items_sorted_by_name (self):
'''All items sorted by name.'''
return sorted(self.props.items(), key=lambda (k,v): str(k).lower())
def _get_tok (self):
'''Get Character.'''
tok = self.lex.get_token()
return tok
def _unget_tok (self, tok):
'''Unget Character.'''
self.lex.unget_token(tok)
def __getitem__ (self, k):
'''Get Properties Property.'''
return self.props[k]
def __iter__ (self):
'''Get Properties Property iterator.'''
return iter(self.props)
def __cmp__ (self, other):
'''Compare two Properties objects (using their Source).'''
return cmp(self.src, other.src)
def __len__ (self):
'''Length of an Properties object is number of Property objects.'''
return len(self.props)
def __str__ (self):
'''String version.'''
ss = map(lambda (k,v): '%s: "%s"\n' % (k,str(v)), self.items_sorted_by_name())
return reduce(lambda acc,s: acc+s, ss, '')
def __repr__ (self):
'''JSON-ish version.'''
k = self.keys_sorted_by_name()
s = '{Properties:{len:%d, keys:%s, src:%s, id:%s}}'
t = (len(self), k, repr(self.src), hex(id(self)))
return s % t
def __init__ (self, filename, LexClass=Lexer):
'''Create a new Properties instance.'''
self.props = {}
self.filename = filename
self.src = SourceFile(self.filename)
self.lex = LexClass(self.src)
E1 = "Parser: ERROR *** Oh, No!! There's a Syntax Error! ***"
E2 = "error: %s"
E3 = "file: %s"
E4 = "line: %d, char: %d (chars read: %d)"
class ApplicationProperties (Properties):
'''\
Application Properties Class.
properties:
methods:
[N] - Nth argument
.len() - number of arguments
.str() - string representation
.repr() - JSON-like representation
'''
def _parse_file (self):
tok = self._get_tok()
while tok:
if tok[0] == Lexer.NewlineToken:
tok = self._get_tok()
continue
if tok[0] not in [Lexer.NameToken, Lexer.StringToken]:
raise SyntaxError, ('Expected Property Name. [%s]' % str(tok))
prop_name = tok[1]
tok = self._get_tok()
if tok[0] != Lexer.SymbolToken:
raise SyntaxError, ('Expected delimiter. [%s]' % str(tok))
if tok[1] not in [':', '=']:
raise SyntaxError, ('Unexpected delimiter. [%s]' % str(tok))
ch = self.lex.skip_whitespace()
if ch in self.lex.sdq_chars:
prop_value = self._get_tok()
else:
prop_value = self.lex.get_rest_of_line()
self.props[prop_name] = prop_value[1]
tok = self._get_tok()
def __repr__ (self):
'''JSON-ish version.'''
r = super(ApplicationProperties,self).__repr__()
s = '{ApplicationProperties:{parent:%s, id:%s}}'
t = (r, hex(id(self)))
return s % t
def __init__ (self, filename):
'''Create a new Application Properties instance.'''
super(ApplicationProperties,self).__init__(filename, LexerLine)
try:
self._parse_file()
except IndexError:
pass
except SyntaxError as e:
print >> stderr, Properties.E1
print >> stderr, Properties.E2 % str(e)
print >> stderr, Properties.E3 % self.src.filename
print >> stderr, Properties.E4 % (self.src.lines, self.src.cp, self.src.chars)
class ObjectProperties (Properties):
'''\
Object Properties Class.
properties:
methods:
[N] - Nth argument
.len() - number of arguments
.str() - string representation
.repr() - JSON-like representation
'''
def _parse_file (self):
tok = self._get_tok()
while tok:
if tok[0] == Lexer.NewlineToken:
tok = self._get_tok()
continue
if tok[0] not in [Lexer.NameToken, Lexer.StringToken]:
raise SyntaxError, ('Expected Property Name. [%s]' % str(tok))
prop_name = tok[1]
tok = self._get_tok()
if tok[0] != Lexer.SymbolToken:
raise SyntaxError, ('Expected delimiter. [%s]' % str(tok))
if tok[1] not in [':', '=']:
raise SyntaxError, ('Unexpected delimiter. [%s]' % str(tok))
prop_values = []
tok = self._get_tok()
while tok[0] != Lexer.NewlineToken:
if tok[0] in [Lexer.NumberToken, Lexer.DateToken]:
val = tok[2]
else:
val = tok[1]
prop_values.append(val)
tok = self._get_tok()
self.props[prop_name] = tuple([len(prop_values)]+prop_values)
tok = self._get_tok()
def __repr__ (self):
'''JSON-ish version.'''
r = super(ObjectProperties,self).__repr__()
s = '{ObjectProperties:{parent:%s, id:%s}}'
t = (r, hex(id(self)))
return s % t
def __init__ (self, filename):
'''Create a new Properties instance.'''
super(ObjectProperties,self).__init__(filename)
try:
self._parse_file()
except IndexError:
pass
except SyntaxError as e:
print >> stderr, Properties.E1
print >> stderr, Properties.E2 % str(e)
print >> stderr, Properties.E3 % self.src.filename
print >> stderr, Properties.E4 % (self.src.lines, self.src.cp, self.src.chars)
class QueryProperties (Properties):
'''\
Query Properties Class.
properties:
.text
.args
methods:
[N] - Nth argument
.len() - number of arguments
.str() - string representation
.repr() - JSON-like representation
'''
def _parse_file (self):
'''| <file> := [<property-name> <property-value>]* |'''
tok = self._get_tok()
while tok:
if tok[0] == Lexer.NameToken:
prop_name = tok[1]
prop_value = self._parse_prop_value()
if prop_name not in self.props:
self.props[prop_name] = [prop_value]
else:
self.props[prop_name].append(prop_value)
tok = self._get_tok()
continue
raise SyntaxError, ('Unexpected token. [%s]' % str(tok))
raise RuntimeError, 'Unexpected Token loop exit!'
def _parse_prop_value (self):
'''| <property-value> := "(" <item> [, <item>]* ")" |'''
tok = self._get_tok()
while tok:
if tok[1] == '(':
return self._parse_value_list()
raise SyntaxError, ('Expected "(" to begin property value! [%s]' % str(tok))
raise RuntimeError, 'Unexpected Token loop exit!'
def _parse_value_list (self):
'''| <item> ")" or <item> "," |'''
tok = self._get_tok()
if tok[1] == ')':
return []
prop_values = []
while tok:
val = self._parse_value_item(tok)
prop_values.append(val)
tok = self._get_tok()
if tok[1] == ',':
tok = self._get_tok()
continue
if tok[1] == ')':
return prop_values
raise SyntaxError, ('Expected ")" to end property value! [%s]' % str(tok))
raise RuntimeError, 'Unexpected Token loop exit!'
def _parse_value_item (self, val_tok):
'''| <item> := <constant> or <name> or <sub-property> |'''
if val_tok[0] in [Lexer.NumberToken, Lexer.DateToken]:
return (val_tok[3], val_tok[2])
if val_tok[0] == Lexer.StringToken:
return ('string', val_tok[1])
if val_tok[0] == Lexer.NameToken:
prop_name = val_tok[1]
tok = self._get_tok()
if tok[1] == '(':
prop_value = self._parse_value_list()
return ('f:'+prop_name, prop_value)
self._unget_tok(tok)
return ('name', prop_name)
raise SyntaxError, ('Expected value token! [%s]' % str(val_tok))
def __str__ (self):
'''String version.'''
s1 = '%s:\n%s'
s2 = ' %s\n'
ss = map(lambda (k,v): s1 % (k, ListToString(map(lambda m: s2 % str(m), v))), self.items_sorted_by_name())
return ListToString(ss)
def __repr__ (self):
'''JSON-ish version.'''
r = super(QueryProperties,self).__repr__()
s = '{QueryProperties:{parent:%s, id:%s}}'
t = (r, hex(id(self)))
return s % t
def __init__ (self, filename):
'''Create a new Properties instance.'''
super(QueryProperties,self).__init__(filename)
self.lex.nl_flag = False
try:
self._parse_file()
except IndexError:
pass
except SyntaxError as e:
print >> stderr, Properties.E1
print >> stderr, Properties.E2 % str(e)
print >> stderr, Properties.E3 % self.src.filename
print >> stderr, Properties.E4 % (self.src.lines, self.src.cp, self.src.chars)
'''eof'''