Create a python version of qfplist.

I've decided to use property lists to define mdl control scripts. Some
names will probably get changed, and I still need to write code for writing
a plist, but the hard part is pretty much done :)
This commit is contained in:
Bill Currie 2012-04-17 21:29:27 +09:00
parent 561484842c
commit 68bf0108fb

View file

@ -0,0 +1,208 @@
# vim:ts=4:et
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
quotables = ("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^")
class pldata:
def __init__(self, src):
self.src = src
self.pos = 0;
self.end = len (self.src)
self.error = None
self.line = 1
def skip_space(self):
while self.pos < self.end:
c = self.src[self.pos]
if not c.isspace():
if c == '/' and self.pos < self.end - 1: #comments
if self.src[self.pos + 1] == '/': # // coment
self.pos += 2
while self.pos < self.end:
c = self.src[self.pos]
if c == '\n':
break
self.pos += 1
if self.pos >= self.end:
self.error = "Reached end of string in comment"
return False
elif self.src[self.pos + 1] == '*': # /* comment */
self.pos += 2
while self.pos < self.end:
c = self.src[self.pos]
if c == '\n':
self.line += 1
elif (c == '*' and self.pos < self.end - 1
and self.src[self.pos + 1] == '/'):
self.pos += 1
break
self.pos += 1
if self.pos >= self.end:
self.error = "Reached end of string in comment"
return False
else:
return True
else:
return True
if c == '\n':
self.line += 1
self.pos += 1
self.error = "Reached end of string"
def parse_quoted_string(self):
long_string = False
escaped = 0
shrink = 0
hexa = False
self.pos += 1
start = self.pos
if (self.pos < self.end - 1 and self.src[self.pos] == '"'
and self.src[self.pos + 1] == '"'):
self.pos += 2
long_string = True
start += 2
while self.pos < self.end:
c = self.src[self.pos]
if escaped:
if escaped == 1 and c == '0':
escaped += 1
hexa = False
elif escaped > 1:
if escaped == 2 and c == 'x':
hexa = True
shring += 1
escaped += 1
elif hex and c.isxdigit():
shrink += 1
escaped += 1
elif c in range(0, 8):
shrink += 1
escaped += 1
else:
self.pos -= 1
escaped = 0
else:
escaped = 0
else:
if c == '\\':
escaped = 1
shrink += 1
elif (c == '"'
and (not long_string
or (self.pos < self.end - 2
and self.src[self.pos + 1] == '"'
and self.src[self.pos + 2] == '"'))):
break
if c == '\n':
self.line += 1
self.pos += 1
if self.pos >= self.end:
self.error = "Reached end of string while parsing quoted string"
return None
if self.pos - start - shrink == 0:
return ""
s = self.src[start:self.pos]
self.pos += 1
if long_string:
self.pos += 2
return eval('"""' + s + '"""')
def parse_unquoted_string(self):
start = self.pos
while self.pos < self.end:
if self.src[self.pos] not in quotables:
break
self.pos += 1
return self.src[start:self.pos]
def parse_data(self):
self.pos += 1
start = self.pos
nibbles = 0
while self.pos < self.end:
if self.src[self.pos].isxdigit:
nibbles += 1
self.pos += 1
continue
if self.src[self.pos] == '>':
if nibbles & 1:
self.error = "invalid data, missing nibble"
return None
s = self.src[start:self.pos]
self.pos += 1
return binascii.a2b_hex(s)
self.error = "invalid character in data"
return None
self.error = "Reached end of string while parsing data"
return None
def parse(self):
if not self.skip_space():
return None
if self.src[self.pos] == '{':
item = {}
self.pos += 1
while self.skip_space() and self.src[self.pos] != '}':
key = self.parse()
if key == None:
return None
if type(key) != str:
self.error = "Key is not a string"
return None
if not self.skip_space():
return None
if self.src[self.pos] != '=':
self.error = "Unexpected character (expected '=')"
return None
self.pos += 1
value = self.parse()
if not value:
return None
if self.src[self.pos] == ';':
self.pos += 1
elif self.src[self.pos] != '}':
self.error = "Unexpected character (wanted ';' or '}')"
return None
item[key] = value
if self.pos >= self.end:
self.error = "Unexpected end of string when parsing dictionary"
return None
self.pos += 1
return item
elif self.src[self.pos] == '(':
item = []
self.pos += 1
while self.skip_space() and self.src[self.pos] != ')':
value = self.parse()
if value == None:
return None
if not self.skip_space():
return None
if self.src[self.pos] == ',':
self.pos += 1
elif self.src[self.pos] != ')':
self.error = "Unexpected character (wanted ',' or ')')"
return None
item.append(value)
self.pos += 1
return item
elif self.src[self.pos] == '<':
return self.parse_data()
elif self.src[self.pos] == '"':
return self.parse_quoted_string()
else:
return self.parse_unquoted_string()