--- a/tools/configcollector.py Tue Jun 14 17:55:50 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ -#!/usr/bin/env python3 -# coding: utf-8 -# -# Copyright 2015 - 2017 Teemu Piippo -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER -# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -from argparse import ArgumentParser -from collections import OrderedDict -import caseconversions -import outputfile - -# These types are passed by value -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' - elif value.startswith(('"', 'R"')) and value.endswith('"'): - return 'QString' - - try: - int(value) - return 'int' - except: - pass - - try: - float(value) - return 'double' - except: - pass - - if value.endswith('f'): - try: - float(value[:-1]) - return 'float' - except: - pass - - raise ValueError('unable to deduce type of %r' % value) - -class ConfigCollector: - def __init__(self, args): - self.declarations = OrderedDict() - self.qtTypes = set() - self.args = args - - def collect(self, filename): - with open(filename) as file: - for linenumber, line in enumerate(file, 1): - try: - line = line.strip() - if line and not line.startswith('#'): - from re import search - match = search('^option (\w+) = (.+)$', line) - if not match: - raise ValueError('unable to parse: %r' % line) - name, value = match.groups() - match = search(r'^([a-zA-Z0-9_<>]+)\s*\{(.*)\}$', value) - try: - typename = match.group(1) - except: - typename = deduce_type(value) - self.declare(name, typename, value) - except ValueError as error: - from sys import stderr, exit - print(str.format( - '{file}:{line}: {error}', - file = filename, - line = linenumber, - error = str(error), - ), file = stderr) - exit(1) - # Sort the declarations in alphabetical order - self.declarations = OrderedDict(sorted(self.declarations.items(), key = lambda t: t[1]['name'])) - # Fill in additional information - for declaration in self.declarations.values(): - declaration['readgate'] = caseconversions.convert_case(declaration['name'], style='java') - declaration['writegate'] = 'set' + caseconversions.convert_case(declaration['name'], style='camel') - declaration['togglefunction'] = 'toggle' + caseconversions.convert_case(declaration['name'], style='camel') - if declaration['type'] in passbyvalue: - declaration['typereference'] = declaration['type'] - else: - declaration['typereference'] = 'const %s&' % declaration['type'] - - def declare(self, name, typename, default): - from re import findall - if name in self.declarations: - raise ValueError('Attempted to redeclare %r' % name) - self.declarations[name] = { - 'name': name, - 'type': typename, - 'default': default - } - # Keep a file of any Qt types, we'll need to #include them. - self.qtTypes.update(findall(r'Q\w+', typename)) - - def writeHeader(self, device): - device.write('#pragma once\n') - device.write('#include <QMap>\n') - device.write('#include <QSettings>\n') - device.write('#include <glm/glm.hpp>\n') - for qtType in sorted(self.qtTypes): - device.write('#include <%s>\n' % qtType) - device.write('#include "libraries.h"\n') - device.write('\n') - formatargs = {} - write = lambda value: device.write(value) - write('class Configuration : private QSettings\n') - write('{\n') - write('public:\n') - write('\tusing QSettings::QSettings;\n'); - write('\tbool exists(const QString& name);\n') - write('\tQVariant value(const QString& name);\n') - write('\tQVariant setValue(const QString& name);\n') - for declaration in self.declarations.values(): - write('\t{type} {readgate}();\n'.format(**declaration)) - for declaration in self.declarations.values(): - write('\tvoid {writegate}({typereference} value);\n'.format(**declaration)) - for declaration in filter(lambda declaration: declaration['type'] == 'bool', self.declarations.values()): - write('\tvoid {togglefunction}();\n'.format(**declaration)) - write('private:\n') - write('\tusing QSettings::value;\n') - write('\tusing QSettings::setValue;\n') - write('\tstatic const QMap<QString, QVariant>& defaults();\n') - write('};\n') - - def writeSource(self, device, headername): - device.write('#include <QSet>\n') - device.write('#include <QSettings>\n') - device.write('#include <QVariant>\n') - device.write('#include "%s/mainwindow.h"\n' % (self.args.sourcedir)) - device.write('#include "%s"\n' % headername) - device.write( - 'const QMap<QString, QVariant>& Configuration::defaults()\n' - '{\n' - '\tstatic const QMap<QString, QVariant> defaults {\n' - ) - for declaration in self.declarations.values(): - device.write('\t\t{{"{name}", QVariant::fromValue<{type}>({default})}},\n'.format(**declaration)) - device.write('\t};\n' - '\treturn defaults;\n' - '}\n' - '\n') - device.write('bool Configuration::exists(const QString& name)\n') - device.write('{\n') - device.write('\treturn defaults().contains(name);\n') - device.write('}\n') - device.write('\n') - device.write('QVariant Configuration::value(const QString& name)\n' - '{\n' - '\treturn this->value(name, Configuration::defaults().value(name));\n' - '}\n') - device.write('\n') - for declaration in self.declarations.values(): - device.write('{type} Configuration::{readgate}()\n'.format(**declaration)) - device.write('{\n') - device.write('\tstatic const QVariant defaultvalue = QVariant::fromValue<{type}>({default});\n'.format(**declaration)) - device.write('\treturn this->value("{name}", defaultvalue).value<{type}>();\n'.format(**declaration)) - device.write('}\n') - device.write('\n') - for declaration in self.declarations.values(): - device.write('void Configuration::{writegate}({typereference} value)\n'.format(**declaration)) - device.write('{\n') - device.write('\tif (value != {default})\n'.format(**declaration)) - device.write('\t\tthis->setValue("{name}", QVariant::fromValue<{type}>(value));\n'.format(**declaration)) - device.write('\telse\n') - device.write('\t\tthis->remove("{name}");\n'.format(**declaration)) - device.write('}\n') - device.write('\n') - for declaration in filter(lambda declaration: declaration['type'] == 'bool', self.declarations.values()): - device.write('void Configuration::{togglefunction}()\n'.format(**declaration)) - device.write('{\n') - device.write('\t{writegate}(not {readgate}());\n'.format(**declaration)) - device.write('}\n') - device.write('\n') - -def main(): - parser = ArgumentParser(description='Collects a list of configuration objects') - 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.input) - from outputfile import OutputFile - header = OutputFile(args.header) - source = OutputFile(args.source) - collector.writeSource(source, headername=args.header) - collector.writeHeader(header) - header.save(verbose = True) - source.save(verbose = True) - -if __name__ == '__main__': - main()