[glib/new-gsettings] Make the simple schema parser a class
- From: Vincent Untz <vuntz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/new-gsettings] Make the simple schema parser a class
- Date: Sat, 17 Apr 2010 16:00:37 +0000 (UTC)
commit fb19c20ca1d874aab38df3ca88a9a776a6f26bfd
Author: Vincent Untz <vuntz gnome org>
Date: Sat Apr 17 11:13:12 2010 -0400
Make the simple schema parser a class
This makes it easier to track the current state, and, for example, put
the line number in error messages.
gio/gsettings-schema-convert | 424 +++++++++++++++++++++++-------------------
1 files changed, 231 insertions(+), 193 deletions(-)
---
diff --git a/gio/gsettings-schema-convert b/gio/gsettings-schema-convert
index b10080c..b0c8a9d 100755
--- a/gio/gsettings-schema-convert
+++ b/gio/gsettings-schema-convert
@@ -418,242 +418,279 @@ class GConfSchema:
######################################
-allowed_tokens = {
- '' : [ 'gettext-domain', 'schema' ],
- 'gettext-domain' : [ ],
- 'schema' : [ 'gettext-domain', 'path', 'child', 'key' ],
- 'path' : [ ],
- 'child' : [ 'gettext-domain', 'child', 'key' ],
- 'key' : [ 'l10n', 'summary', 'description', 'choices', 'range' ],
- 'l10n' : [ ],
- 'summary' : [ ],
- 'description' : [ ],
- 'choices' : [ ],
- 'range' : [ ]
-}
-
-allowed_separators = [ ':', '=' ]
-
-def _eat_indent(line, indent_stack):
- i = 0
- buf = ''
- previous_max_index = len(indent_stack) - 1
- index = -1
-
- while i < len(line) - 1 and line[i].isspace():
- buf += line[i]
- i += 1
- if previous_max_index > index:
- if buf == indent_stack[index + 1]:
- buf = ''
- index += 1
- continue
- elif indent_stack[index + 1].startswith(buf):
- continue
+
+class SimpleSchemaParser:
+
+ allowed_tokens = {
+ '' : [ 'gettext-domain', 'schema' ],
+ 'gettext-domain' : [ ],
+ 'schema' : [ 'gettext-domain', 'path', 'child', 'key' ],
+ 'path' : [ ],
+ 'child' : [ 'gettext-domain', 'child', 'key' ],
+ 'key' : [ 'l10n', 'summary', 'description', 'choices', 'range' ],
+ 'l10n' : [ ],
+ 'summary' : [ ],
+ 'description' : [ ],
+ 'choices' : [ ],
+ 'range' : [ ]
+ }
+
+ allowed_separators = [ ':', '=' ]
+
+ def __init__(self, file):
+ self.file = file
+
+ self.root = GSettingsSchemaRoot()
+
+ # this is just a convenient helper to remove the leading indentation
+ # that should be common to all lines
+ self.leading_indent = None
+
+ self.indent_stack = []
+ self.token_stack = []
+ self.object_stack = [ self.root ]
+
+ self.previous_token = None
+ self.current_token = None
+ self.unparsed_line = ''
+
+ def _eat_indent(self):
+ line = self.unparsed_line
+ i = 0
+ buf = ''
+ previous_max_index = len(self.indent_stack) - 1
+ index = -1
+
+ while i < len(line) - 1 and line[i].isspace():
+ buf += line[i]
+ i += 1
+ if previous_max_index > index:
+ if buf == self.indent_stack[index + 1]:
+ buf = ''
+ index += 1
+ continue
+ elif self.indent_stack[index + 1].startswith(buf):
+ continue
+ else:
+ raise GSettingsSchemaConvertException('Inconsistent indentation.')
else:
- raise GSettingsSchemaConvertException('Inconsistent indentation.')
- else:
- continue
-
- if buf and previous_max_index > index:
- raise GSettingsSchemaConvertException('Inconsistent indentation.')
- elif buf and previous_max_index <= index:
- indent_stack.append(buf)
- elif previous_max_index > index:
- indent_stack = indent_stack[:index + 1]
-
- return (indent_stack, line[i:])
-
-def _eat_word(line):
- i = 0
- while i < len(line) and not line[i].isspace() and not line[i] in allowed_separators:
- i += 1
- return (line[:i], line[i:])
-
-def _word_to_token(word):
- lower = word.lower()
- if lower and lower in allowed_tokens.keys():
- return lower
- raise GSettingsSchemaConvertException('\'%s\' is not a valid token.' % lower)
-
-def _token_allow_separator(token):
- return token in [ 'gettext-domain', 'path', 'l10n', 'summary', 'description', 'choices', 'range' ]
-
-def _get_name_without_separator(line, token):
- if line[-1] in allowed_separators:
- line = line[:-1].strip()
- # FIXME: we could check there's no space
- return line
-
-def _parse_key(line):
- split = False
- for separator in allowed_separators:
- items = line.split(separator)
- if len(items) == 2:
- split = True
- break
-
- if not split:
- raise GSettingsSchemaConvertException('Key \'%s\' cannot be parsed.' % line)
-
- # FIXME: we could check there's no space
- name = items[0].strip()
- type = ''
- value = items[1].strip()
- if value[0] == '@':
- i = 1
- while not value[i].isspace():
+ continue
+
+ if buf and previous_max_index > index:
+ raise GSettingsSchemaConvertException('Inconsistent indentation.')
+ elif buf and previous_max_index <= index:
+ self.indent_stack.append(buf)
+ elif previous_max_index > index:
+ self.indent_stack = self.indent_stack[:index + 1]
+
+ self.unparsed_line = line[i:]
+
+ def _parse_word(self):
+ line = self.unparsed_line
+ i = 0
+ while i < len(line) and not line[i].isspace() and not line[i] in self.allowed_separators:
i += 1
- type = value[1:i]
- value = value[i:].strip()
- if not value:
- raise GSettingsSchemaConvertException('No value specified for key \'%s\' (\'%s\').' % (name, line))
- return (name, type, value)
-
-def _parse_l10n(line):
- items = [ item.strip() for item in line.split(' ') if item.strip() ]
- if not items:
- return (None, None)
- if len(items) == 1:
- return (items[0], None)
- if len(items) == 2:
- return (items[0], items[1])
-
- raise GSettingsSchemaConvertException('Localization \'%s\' cannot be parsed.' % line)
-
-def _parse_choices(line, type, keyname):
- if type in TYPES_FOR_CHOICES:
- return [ item.strip() for item in line.split(',') ]
- else:
- raise GSettingsSchemaConvertException('Key \'%s\' of type \'%s\' cannot have choices.' % (keyname, type))
-
-def _parse_range(line, type, keyname):
- minmax = None
- if type in TYPES_FOR_RANGE:
- minmax = [ item.strip() for item in line.split('..') ]
- if len(minmax) != 2:
- raise GSettingsSchemaConvertException('Range \'%s\' cannot be parsed.' % line)
- # FIXME: we'll be able to check min < max once we can convert the
- # values with GVariant
- return tuple(minmax)
- else:
- raise GSettingsSchemaConvertException('Key \'%s\' of type \'%s\' cannot have a range.' % (keyname, type))
-
-def read_simple_schema(simple_schema_file):
- root = GSettingsSchemaRoot()
-
- f = open(simple_schema_file, 'r')
- lines = f.readlines()
- f.close()
- lines = [ line[:-1] for line in lines ]
-
- leading_indent = None
- indent_stack = []
- token_stack = []
- object_stack = [ root ]
-
- token = None
- previous_token = None
-
- for line in lines:
+ self.unparsed_line = line[i:]
+ return line[:i]
+
+ def _word_to_token(self, word):
+ lower = word.lower()
+ if lower and lower in self.allowed_tokens.keys():
+ return lower
+ raise GSettingsSchemaConvertException('\'%s\' is not a valid token.' % lower)
+
+ def _token_allow_separator(self):
+ return self.current_token in [ 'gettext-domain', 'path', 'l10n', 'summary', 'description', 'choices', 'range' ]
+
+ def _parse_name_without_separator(self):
+ line = self.unparsed_line
+ if line[-1] in self.allowed_separators:
+ line = line[:-1].strip()
+ self.unparsed_line = ''
+ # FIXME: we could check there's no space
+ return line
+
+ def _parse_key(self):
+ line = self.unparsed_line
+
+ split = False
+ for separator in self.allowed_separators:
+ items = line.split(separator)
+ if len(items) == 2:
+ split = True
+ break
+
+ if not split:
+ raise GSettingsSchemaConvertException('Key \'%s\' cannot be parsed.' % line)
+
+ # FIXME: we could check there's no space
+ name = items[0].strip()
+ type = ''
+ value = items[1].strip()
+ if value[0] == '@':
+ i = 1
+ while not value[i].isspace():
+ i += 1
+ type = value[1:i]
+ value = value[i:].strip()
+ if not value:
+ raise GSettingsSchemaConvertException('No value specified for key \'%s\' (\'%s\').' % (name, line))
+
+ self.unparsed_line = ''
+
+ object = GSettingsSchemaKey()
+ object.name = name
+ object.type = type
+ object.default = value
+
+ return object
+
+ def _parse_l10n(self):
+ line = self.unparsed_line
+
+ items = [ item.strip() for item in line.split(' ') if item.strip() ]
+ if not items:
+ self.unparsed_line = ''
+ return (None, None)
+ if len(items) == 1:
+ self.unparsed_line = ''
+ return (items[0], None)
+ if len(items) == 2:
+ self.unparsed_line = ''
+ return (items[0], items[1])
+
+ raise GSettingsSchemaConvertException('Localization \'%s\' cannot be parsed.' % line)
+
+ def _parse_choices(self, object):
+ if object.type in TYPES_FOR_CHOICES:
+ line = self.unparsed_line
+ result = [ item.strip() for item in line.split(',') ]
+ self.unparsed_line = ''
+ return result
+ else:
+ raise GSettingsSchemaConvertException('Key \'%s\' of type \'%s\' cannot have choices.' % (object.name, object.type))
+
+ def _parse_range(self, object):
+ minmax = None
+ if object.type in TYPES_FOR_RANGE:
+ line = self.unparsed_line
+ minmax = [ item.strip() for item in line.split('..') ]
+ if len(minmax) != 2:
+ raise GSettingsSchemaConvertException('Range \'%s\' cannot be parsed.' % line)
+ # FIXME: we'll be able to check min < max once we can convert the
+ # values with GVariant
+ self.unparsed_line = ''
+ return tuple(minmax)
+ else:
+ raise GSettingsSchemaConvertException('Key \'%s\' of type \'%s\' cannot have a range.' % (object.name, object.type))
+
+ def parse_line(self, line):
# make sure that lines with only spaces are ignored and considered as
# empty lines
- line = line.rstrip()
+ self.unparsed_line = line.rstrip()
# ignore empty line
- if not line:
- continue
+ if not self.unparsed_line:
+ return
# look at the indentation to know where we should be
- (indent_stack, line) = _eat_indent(line, indent_stack)
- if leading_indent is None:
- leading_indent = len(indent_stack)
+ self._eat_indent()
+ if self.leading_indent is None:
+ self.leading_indent = len(self.indent_stack)
# ignore comments
- if line[0] == '#':
- continue
+ if self.unparsed_line[0] == '#':
+ return
- (word, line) = _eat_word(line)
- if token:
- previous_token = token
- token = _word_to_token(word)
- line = line.lstrip()
+ word = self._parse_word()
+ if self.current_token:
+ self.previous_token = self.current_token
+ self.current_token = self._word_to_token(word)
+ self.unparsed_line = self.unparsed_line.lstrip()
- allow_separator = _token_allow_separator(token)
- if len(line) > 0 and line[0] in allowed_separators:
+ allow_separator = self._token_allow_separator()
+ if len(self.unparsed_line) > 0 and self.unparsed_line[0] in self.allowed_separators:
if allow_separator:
- line = line[1:]
- line = line.lstrip()
+ self.unparsed_line = self.unparsed_line[1:].lstrip()
else:
- raise GSettingsSchemaConvertException('Separator \'%s\' is not allowed after \'%s\'.' % (line[0], token))
+ raise GSettingsSchemaConvertException('Separator \'%s\' is not allowed after \'%s\'.' % (self.unparsed_line[0], self.current_token))
- new_level = len(indent_stack) - leading_indent
- old_level = len(token_stack)
+ new_level = len(self.indent_stack) - self.leading_indent
+ old_level = len(self.token_stack)
if new_level > old_level + 1:
raise GSettingsSchemaConvertException('Internal error: stacks not in sync.')
elif new_level <= old_level:
- token_stack = token_stack[:new_level]
+ self.token_stack = self.token_stack[:new_level]
# we always have the root
- object_stack = object_stack[:new_level + 1]
+ self.object_stack = self.object_stack[:new_level + 1]
if new_level == 0:
parent_token = ''
else:
- parent_token = token_stack[-1]
+ parent_token = self.token_stack[-1]
# there's new indentation, but no token is allowed under the previous
# one
- if new_level == old_level + 1 and previous_token != parent_token:
- raise GSettingsSchemaConvertException('\'%s\' is not allowed under \'%s\'.' % (token, previous_token))
+ if new_level == old_level + 1 and self.previous_token != parent_token:
+ raise GSettingsSchemaConvertException('\'%s\' is not allowed under \'%s\'.' % (self.current_token, self.previous_token))
- if not token in allowed_tokens[parent_token]:
+ if not self.current_token in self.allowed_tokens[parent_token]:
if parent_token:
- error = '\'%s\' is not allowed under \'%s\'.' % (token, parent_token)
+ error = '\'%s\' is not allowed under \'%s\'.' % (self.current_token, parent_token)
else:
- error = '\'%s\' is not allowed at the root level.' % token
+ error = '\'%s\' is not allowed at the root level.' % self.current_token
raise GSettingsSchemaConvertException(error)
- current_object = object_stack[-1]
+ current_object = self.object_stack[-1]
new_object = None
- if token == 'gettext-domain':
- current_object.gettext_domain = line
- elif token == 'schema':
- name = _get_name_without_separator(line, token)
+ if self.current_token == 'gettext-domain':
+ current_object.gettext_domain = self.unparsed_line
+ elif self.current_token == 'schema':
+ name = self._parse_name_without_separator()
new_object = GSettingsSchema()
new_object.id = name
current_object.schemas.append(new_object)
- elif token == 'path':
- current_object.path = line
- elif token == 'child':
- name = _get_name_without_separator(line, token)
+ elif self.current_token == 'path':
+ current_object.path = self.unparsed_line
+ elif self.current_token == 'child':
+ name = self._parse_name_without_separator()
new_object = GSettingsSchemaDir()
new_object.name = name
current_object.dirs.append(new_object)
- elif token == 'key':
- new_object = GSettingsSchemaKey()
- (name, type, value) = _parse_key(line)
- new_object.name = name
- new_object.type = type
- new_object.default = value
+ elif self.current_token == 'key':
+ new_object = self._parse_key()
current_object.keys.append(new_object)
- elif token == 'l10n':
- (current_object.l10n, current_object.l10n_context) = _parse_l10n(line)
- elif token == 'summary':
- current_object.summary = line
- elif token == 'description':
- current_object.description = line
- elif token == 'choices':
- current_object.choices = _parse_choices(line, current_object.type, current_object.name)
- elif token == 'range':
- current_object.range = _parse_range(line, current_object.type, current_object.name)
+ elif self.current_token == 'l10n':
+ (current_object.l10n, current_object.l10n_context) = self._parse_l10n()
+ elif self.current_token == 'summary':
+ current_object.summary = self.unparsed_line
+ elif self.current_token == 'description':
+ current_object.description = self.unparsed_line
+ elif self.current_token == 'choices':
+ current_object.choices = self._parse_choices(current_object)
+ elif self.current_token == 'range':
+ current_object.range = self._parse_range(current_object)
if new_object:
- token_stack.append(token)
- object_stack.append(new_object)
+ self.token_stack.append(self.current_token)
+ self.object_stack.append(new_object)
+
+ def parse(self):
+ f = open(self.file, 'r')
+ lines = [ line[:-1] for line in f.readlines() ]
+ f.close()
+
+ try:
+ current_line_nb = 0
+ for line in lines:
+ current_line_nb += 1
+ self.parse_line(line)
+ except GSettingsSchemaConvertException, e:
+ raise GSettingsSchemaConvertException('%s:%s: %s' % (os.path.basename(self.file), current_line_nb, e))
- return root
+ return self.root
######################################
@@ -796,7 +833,8 @@ def main(args):
except SyntaxError, e:
raise GSettingsSchemaConvertException('\'%s\' does not look like a valid gconf schema file: %s' % (argfile, e))
else:
- schema_root = read_simple_schema(argfile)
+ parser = SimpleSchemaParser(argfile)
+ schema_root = parser.parse()
if options.xml:
node = schema_root.get_xml_node()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]