tools/configcollector.py

changeset 1125
d8f94e56d42e
parent 1017
fc1c13db9618
child 1126
508950e27fcd
--- a/tools/configcollector.py	Sun Feb 12 16:02:44 2017 +0200
+++ b/tools/configcollector.py	Sun Feb 12 16:59:35 2017 +0200
@@ -1,7 +1,7 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # coding: utf-8
 #
-#	Copyright 2015 Teemu Piippo
+#	Copyright 2015 - 2017 Teemu Piippo
 #	All rights reserved.
 #
 #	Redistribution and use in source and binary forms, with or without
@@ -37,117 +37,79 @@
 import re
 from pprint import pprint
 
-passbyvalue = {'int', 'bool', 'float', 'double' }
-variantconversions = {
-	'QString': 'toString',
-	'bool': 'toBool',
-	'int': 'toInt',
-	'float': 'toFloat',
-	'double': 'toDouble',
-	'QChar': 'toChar',
-	'QBitArray': 'toBitArray',
-	'QDate': 'toDate',
-	'QDateTime': 'toDateTime',
-	'uint': 'toUInt',
-	'unsigned int': 'toUInt',
-	'QUrl': 'toUrl',
-	'QTime': 'toTime',
-	'QPoint': 'toPoint',
-	'QPointF': 'toPointF',
-	'QSize': 'toSize',
-	'QSizeF': 'toSizeF',
-	'qreal': 'toReal',
-	'QRect': 'toRect',
-	'QRectF': 'toRectF',
-	'QLine': 'toLine',
-	'QLineF': 'toLineF',
-	'QEasingCurve': 'toEasingCurve',
-	'qlonglong': 'toLongLong',
-	'qulonglong': 'toULongLong',
-}
+passbyvalue = {'int', 'bool', 'float', 'double', 'qreal'}
+
+def deduce_type(value):
+	'''
+		Try to determine the type of value from the value itself.
+	'''
+	if value in ('true', 'false'):
+		return 'bool'
+
+	if value.startswith('"') and value.endswith('"'):
+		return 'QString'
+
+	try:
+		int(value)
+		return 'int'
+	except:
+		pass
+
+	try:
+		float(value)
+		return 'double'
+	except:
+		pass
+
+	if endswith(value, 'f'):
+		try:
+			float(value[:-1])
+			return 'float'
+		except:
+			pass
+
+	raise ValueError('unable to deduce type of %r' % value)
 
 class ConfigCollector (object):
 	def __init__ (self, args):
-		self.pattern = re.compile (r'\s*ConfigOption\s*\((.+)\)\s*')
-		self.declpattern = re.compile (r'^([A-Za-z0-9,<>\[\]\(\)\{\}\s]+)\s+(\w+)(\s*=\s*(.+))?$')
 		self.decls = []
 		self.qttypes = set()
 		self.args = args
 
-	def collect (self, filenames):
-		for filename in filenames:
-			with open (filename, 'r') as fp:
-				lines = fp.read().splitlines()
-
-			for line in lines:
-				matches = self.pattern.findall (line)
-				for match in matches:
-					match = match.strip()
-					declarations = self.declpattern.findall (match)
-					for decl in declarations:
-						self.add_config_declaration (decl)
+	def collect (self, filename):
+		with open(filename, 'r') as file:
+			for line in file:
+				line = line.strip()
+				if line and not line.startswith('#'):
+					match = re.search('^option (\w+) = (.+)$', line)
+					if not match:
+						raise ValueError('unable to parse: %r' % line)
+					name, value = match.groups()
+					match = re.search(r'^(\w+)\s*\{(.*)\}$', value)
+					try:
+						type, value = match.groups()
+						if not value:
+							value = type + ' {}'
+					except:
+						type = deduce_type(value)
+					self.add_config_declaration((name, type, value))
 
 		self.decls.sort (key=lambda x: x['name'].upper())
 
-	def add_config_declaration (self, decl):
-		decltype, declname, junk, decldefault = decl
-		if not decldefault:
-			if decltype == 'int':
-				decldefault = '0'
-			elif decltype == 'float':
-				decldefault = '0.0f'
-			elif decltype == 'double':
-				decldefault = '0.0'
-			elif decltype == 'bool':
-				raise TypeError ('bool entries must provide a default value')
-			else:
-				decldefault = decltype + '()'
-
-		self.decls.append (dict (name=declname, type=decltype, default=decldefault))
-
-		# Take note of any Qt types we may want to #include in our source file (we'll need to #include them).
-		self.qttypes.update (re.findall (r'(Q[A-Za-z]+)', decltype))
-
-	def make_config_key_type (self, basetype):
-		result = 'ConfigTypeKey_' + basetype
-		result = re.sub (r'[^\w]+', '_', result)
-		return result
-
-	def make_enums (self):
-		self.enums = collections.OrderedDict()
-
 		for decl in self.decls:
-			enumname = self.make_config_key_type (decl['type'])
-			decl['enumerator'] = caseconversions.convert_case (decl['name'], style='upper')
-			decl['enumname'] = enumname
 			decl['getter'] = caseconversions.convert_case (decl['name'], style='java')
 			decl['varname'] = 'm_' + decl['getter']
 			decl['setter'] = 'set' + caseconversions.convert_case (decl['name'], style='camel')
 			decl['toggler'] = 'toggle' + caseconversions.convert_case (decl['name'], style='camel')
 			decl['typecref'] = 'const %s&' % decl['type'] if decl['type'] not in passbyvalue else decl['type']
-
-			try:
-				decl['valuefunc'] = variantconversions[decl['type']]
-			except KeyError:
-				decl['valuefunc'] = 'value<' + decl['type'] + '>'
+			decl['valuefunc'] = 'value<' + decl['type'] + '>'
 
-			if enumname not in self.enums:
-				self.enums[enumname] = dict (
-					name = enumname,
-					typename = decl['type'],
-					values = [],
-					numvalues = 0,
-					highestkey = -1)
+	def add_config_declaration (self, decl):
+		declname, decltype, decldefault = decl
+		self.decls.append (dict (name=declname, type=decltype, default=decldefault))
 
-			self.enums[enumname]['values'].append (decl)
-			self.enums[enumname]['numvalues'] += 1
-			self.enums[enumname]['highestkey'] += 1
-
-		self.enums = collections.OrderedDict (sorted (self.enums.items(), key=lambda x: x[0].upper()))
-
-		for name, enum in self.enums.items():
-			for i, value in enumerate (enum['values']):
-				value['index'] = i
+		# Take note of any Qt types we may want to #include in our source file (we'll need to #include them).
+		self.qttypes.update (re.findall (r'Q\w+', decltype))
 
 	def write_header (self, fp):
 		fp.write ('#pragma once\n')
@@ -177,7 +139,6 @@
 		write ('private:\n')
 		write ('\tQMap<QString, QVariant> m_defaults;\n')
 		write ('\tclass QSettings* m_settings;\n')
-
 		write ('};\n')
 
 	def write_source (self, fp, headername):
@@ -246,14 +207,13 @@
 
 def main():
 	parser = argparse.ArgumentParser (description='Collects a list of configuration objects')
-	parser.add_argument ('inputs', nargs='+')
+	parser.add_argument ('input')
 	parser.add_argument ('--header', required=True)
 	parser.add_argument ('--source', required=True)
 	parser.add_argument ('--sourcedir', required=True)
 	args = parser.parse_args()
 	collector = ConfigCollector (args)
-	collector.collect (args.inputs)
-	collector.make_enums()
+	collector.collect (args.input)
 	header = outputfile.OutputFile (args.header)
 	source = outputfile.OutputFile (args.source)
 	collector.write_source (source, headername=args.header)

mercurial