From 45fb75ae50ab66ba4098beec956ee0695916589c Mon Sep 17 00:00:00 2001 From: Lucas Neves Date: Mon, 13 Nov 2017 21:13:06 +0000 Subject: Select: autogenerator for computed.h, propset.h and propget.h. --- src/select/select_generator.py | 832 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 832 insertions(+) create mode 100644 src/select/select_generator.py (limited to 'src/select/select_generator.py') diff --git a/src/select/select_generator.py b/src/select/select_generator.py new file mode 100644 index 0000000..d215f2b --- /dev/null +++ b/src/select/select_generator.py @@ -0,0 +1,832 @@ +# This file is part of LibCSS. +# Licensed under the MIT License, +# http://www.opensource.org/licenses/mit-license.php +# Copyright 2017 Lucas Neves + +import math +import string +import os +from select_config import values, groups +from assets import assets +from overrides import overrides + +def get_tuple(from_var): + """Convert tuples, strings and None into tuple.""" + if type(from_var) is tuple: + return from_var + elif type(from_var) is str: + return (from_var,) + elif from_var is None: + return () + else: + raise TypeError('Value should be either tuple, string or None, ' + + 'received: ' + type(from_var).__name__) + +def shift_star(value_type, prop_name): + """Shift the asterisks from a pointer type to its name. + + Example: `lwc_string** str_array` would become + `lwc_string **str_array` + """ + star_i = value_type.find('*') + v_type = value_type if star_i is -1 else value_type[:star_i] + v_name = prop_name if star_i is -1 else value_type[star_i:] + prop_name + return (v_type, v_name) + +class Text: + """Class for building strings for output files.""" + def __init__(self): + self._lines = [] + self._comment = False + self._esc_nl = False + self._indent = 0 + + name_width = 31 + bits_width = 16 + + def indent(self, value): + """Increase or decrease indent by . + + Args: + value : positive or negative value to be added to + indentation. + """ + self._indent += value + + def comment(self): + """Toggle C-style comment in the output text.""" + comm = self._comment + self._comment = False + self.append(' */' if comm else '/*') + self._comment = not comm + + def escape_newline(self): + """Toggle escape of newline character.""" + self._esc_nl = not self._esc_nl + + def append(self, text=None, pre_formatted=False): + """Append text to file builder. + + Args: + text: + add contents, breaking lines and adding comment + markers or newline escapes as needed. + recursively call this method for list items. + add a new line. + pre_formatted: just add text without preprocessing. + """ + if not text: + self._lines.append('{}{}{}'.format( + '\t' * self._indent, + ' * ' if self._comment else '', + '\t' * (9 - self._indent) + '\\' if self._esc_nl else '')) + return + + if isinstance(text, list): + for t in text: + self.append(t, pre_formatted) + return + + if pre_formatted: + self._lines.append(text) + return + + line_break_before = [ c for c in ' +/' ] + line_break_after = [ c for c in '({[' ] + column_max = 72 if self._esc_nl else 80 + multiline = False + + while text: + line = '\t' * self._indent + if self._comment: + line += ' * ' + prefix_size = (3 if self._comment else 0) + 8 * self._indent + if prefix_size + len(text) <= column_max: + line += text + text = '' + else: + break_index = 0 + for c in (line_break_before + line_break_after): + after = 1 if c in line_break_after else 0 + break_index = max(break_index, + text[:column_max - prefix_size].rfind(c) + after) + break_index = break_index or len(text) + line += text[:break_index].rstrip() + text = text[break_index:].lstrip() + if self._esc_nl: + n_tabs = 9 - self._indent - math.floor(len(line.lstrip()) / 8) + line += '\t' * n_tabs + '\\' + self._lines.append(line) + if text and not self._comment and not multiline: + self.indent(2) + multiline = True + + if multiline: + self.indent(-2) + + def table_line(self): + """Add a sum line for the tables in computed.h""" + self.append('{0:{n}}{0:{b}}{0}'.format( + '---', n=self.name_width, b=self.bits_width)) + + def table_header(self): + """Add a header line for the tables in computed.h""" + self.append('{:{n}}{:{b}}{}'.format( + 'Property', 'Size (bits)', 'Size (bytes)', + n=self.name_width, b=self.bits_width)) + self.table_line() + + def result_line(self): + """Add a result line for the tables in computed.h""" + self.append(' ' * self.name_width + '=' * (self.bits_width + 3)) + + def to_string(self): + """Output contents of file builder as a string.""" + return '\n'.join(self._lines) + +class CSSValue: + """Values to be associated with properties. + + Args: + name : value name (required). + css_type : C type of value (required). + size : value size, in bytes (default: None, for pointers). + defaults : default value (default: 'NULL', for pointers). + The fields below are only needed if the value stores data in the + array of bits (currently only css_unit uses it). + bits_name : name of bits value (default: None). + bits_type : C type of bits value (default: None). + bits_size : value size, in bits (default: None). + bits_defaults : default value (default: '0'). + """ + def __init__(self, name, css_type, size=None, defaults='NULL', + bits_name=None, bits_type=None, + bits_size=None, bits_defaults='0'): + self.name = name + self.type = css_type + self.size = size # `None` means sizeof(ptr) + self.defaults = defaults + self.suffix = '' + self.bits = None if bits_size is None else { + 'name': bits_name, + 'type': bits_type, + 'size': bits_size, + 'defaults': bits_defaults + } + + @property + def is_ptr(self): + """Return True if value is a pointer; False otherwise.""" + return ((self.type, self.name) != shift_star(self.type, self.name)) + +class CSSProperty: + """Class for CSS properties. + + Args: + name : property name (required). + type_size : opcode size, in bits (required). + values : property values (default: None). + To set one value, using the value's defaults: + 'value_name' + To set multiple values, using the values' defaults: + (('value_name',), ('value_name',)) + To override the default of one or multiple values: + (('value_name', 'default'),) + (('value_name', 'default'), ('value_name', 'default')) + condition : condition (opcode value) to get property + values in propget.h (default: None). + defaults : default opcode (default: None) + comments : comments for properties that are stored in + "struct css_computed_{group}", instead of + "struct css_computed_{group}_i (default: None) + NOTE: passing this argument will result in the property being + stored in "struct css_computed_{group}"! + overrides : files for which this property shouldn't + autogenerate content; instead, read entry from from overrides.py + Possible values: + 'get': overrides output to autogenerated_propget.h + 'set': overrides output to autogenerated_propset.h + ('get', 'set'): overrides output to both files. + """ + def __init__(self, name, type_size, values=None, condition=None, + defaults=None, comments=None, override=None): + self.name = name + self.type_size = type_size + self.values = self.make_values(values) + self.defaults = defaults + self.condition = condition + self.override = get_tuple(override) + self.comments = comments + self.__mask = None + + def make_values(self, vals): + """Make list of values for this property.""" + if vals is None: + return [] + elif type(vals) is str: + return self.make_values(((vals,),)) + elif type(vals) is tuple: + val_list = [] + for i, v in enumerate(vals): + for x in values: + if x[0] == v[0]: + value = CSSValue(*x) + if len(v) is 2: + value.defaults = v[1] + if len(vals) > 1: + value.suffix = '_' + string.ascii_lowercase[i] + val_list.append(value) + break + else: + raise ValueError('Value ' + v[0] + ' not found!') + return val_list + else: + raise TypeError('Expected None, str or tuple, got ' + + type(vals).__name__) + + @property + def bits_size(self): + """Size of this property in the bits array.""" + return self.type_size + sum([ v.bits['size'] for v in self.values + if v.bits is not None ]) + + @property + def bytes_size(self): + """Size of this property's values, in bytes (excluding pointers).""" + return sum([ v.size for v in self.values if v.size is not None ]) + + @property + def ptr_size(self): + """Number of values of this property that are pointers.""" + return sum([ 1 for v in self.values if v.size is None ]) + + @property + def size_line(self): + """String for computed.h with the sizes of this property.""" + name = '{:{width}}'.format(self.name, width=Text.name_width) + type_size = '{:>3}'.format(str(self.type_size)) + extra_size = sum([ v.bits['size'] for v in self.values + if v.bits is not None ]) + bits_size = '{:{width}}'.format(type_size + + (' + ' + str(extra_size) if extra_size else ''), + width=Text.bits_width) + vars_size = '{:>3}'.format( + str(self.bytes_size)) if self.bytes_size else '' + ptr = '' + for v in self.values: + if v.size is None: + ptr = 'sizeof(ptr)' + break + + return (name + bits_size + vars_size + + (' + ' if vars_size and ptr else '') + ptr) + + @property + def mask(self): + """Getter for the bitwise mask of this property in the bits array.""" + if self.__mask is None: + raise NameError('Attribute `mask` not set yet!') + return '0x{:x}'.format(self.__mask).lower() + + @mask.setter + def mask(self, val): + """Setter for the bitwise mask of this property in the bits array.""" + if type(val) is not int: + raise TypeError('Value of `mask` must be an integer!') + if val < 0: + raise ValueError('Value of `mask` must be zero or positive!') + self.__mask = val + + @property + def def_undefs(self): + """Return defines and undefs for propget.h and propset.h.""" + defines = [ + '#define {}_INDEX {}'.format(self.name.upper(), self.index), + '#define {}_SHIFT {}'.format(self.name.upper(), self.shift), + '#define {}_MASK {}'.format(self.name.upper(), self.mask) + ] + undefs = [ + '#undef {}_INDEX'.format(self.name.upper()), + '#undef {}_SHIFT'.format(self.name.upper()), + '#undef {}_MASK'.format(self.name.upper()) + ] + return (defines, undefs) + + def get_param_values(self, pointer=False): + """Make parameters for functions in propget.h and propset.h. + + Args: + pointer : add a star before value name. + """ + vals = [] + for v in self.values: + vt, vn = shift_star(v.type, v.name) + vn += v.suffix + if pointer: + vn = '*' + vn + if v.name == 'counter_arr' or v.name == 'content_item': + vt = 'const ' + vt + vals.append((vt, vn)) + if v.bits is not None: + bt, bn = shift_star(v.bits['type'], v.bits['name']) + bn += v.suffix + if pointer: + bn = '*' + bn + vals.append((bt, bn)) + return vals + + def get_bits(self): + """Make vars for the bitwise operations in propget.h and propset.h.""" + bits = [ + { 'letter': v.suffix[1] if v.suffix else v.bits['name'][0], + 'name': v.bits['name'] + v.suffix, + 'size': v.bits['size'] } + for v in self.values if v.bits is not None ] + bits.append({ 'letter': 't', 'size': self.type_size, 'name': 'type' }) + bits_len = sum([ x['size'] for x in bits ]) + comment = '/* {}bit{}: {} : {} */'.format( + bits_len, + ('' if bits_len is 1 else 's'), + ''.join([ b['letter'] * b['size'] for b in bits ]), + ' | '.join([ b['name'] for b in bits ])) + rev_bits = list(reversed(bits)) + type_mask = '0x{:x}'.format( + sum([ 2 ** x for x in range(rev_bits[0]['size']) ])).lower() + shift_list = [ (x['name'], + sum([ b['size'] for b in rev_bits[:(i + 1)] ]), + sum([ 2 ** x for x in range(x['size']) ]) * 2 ** + sum([ b['size'] for b in rev_bits[:(i + 1)] ])) + for i, x in enumerate(rev_bits[1:]) ] + return (type_mask, shift_list, comment) + +class Bin: + """The storage unit for the bits array of properties.""" + def __init__(self, first_object): + self.contents = [ first_object ] + + @property + def size(self): + return sum([ x.bits_size for x in self.contents ]) + + def push(self, obj): + self.contents.append(obj) + +class CSSGroup: + """Group of CSS properties (i.e. style, page, uncommon). + + Args: + config : imported from select_config.py. + """ + def __init__(self, config): + self.name = config['name'] + self.props = [ CSSProperty(*x) for x in config['props'] ] + self.__bits_array = None + + @property + def bits_size(self): + """Sum of all property bits in the bits array.""" + return sum([ p.bits_size for p in self.props ]) + + @property + def bytes_size(self): + """Sum of all property value bytes (excluded pointers).""" + return sum([ p.bytes_size for p in self.props ]) + + @property + def ptr_size(self): + """Sum of all property pointers.""" + return sum([ p.ptr_size for p in self.props ]) + + @property + def bits_array(self): + """Implement a `best fit first` heuristics for the bin packing + of property bits in the bits array.""" + + if self.__bits_array is not None: + return self.__bits_array + + bin_size = 32 # We're using uint32_t as concrete bins. + self.__bits_array = [] + props = sorted(self.props, key=(lambda x: x.bits_size), reverse=True) + + for p in props: + for b in self.__bits_array: + if b.size + p.bits_size <= bin_size: + b.push(p) + p.shift = (bin_size - + sum([ x.bits_size for x in b.contents ])) + break + else: + p.shift = bin_size - p.bits_size + self.__bits_array.append(Bin(p)) + + p.mask = (sum([ 2 ** x for x in range(p.bits_size) ]) * + 2 ** p.shift) + self.__bits_array.sort(key=(lambda x: x.size), reverse=True) + + for i, b in enumerate(self.__bits_array): + for p in b.contents: + p.index = i + + return self.__bits_array + + def get_idot_grp(self): + """Make parameters for accessing bits and values in this group.""" + i_dot = '' if self.name is 'page' else 'i.' + grp = '' if self.name is 'style' else '->{}{}'.format( + '' if self.name is 'page' else i_dot, self.name) + return (i_dot, grp) + + def make_computed_h(self): + """Output this group's text for the computed.h file.""" + t = Text() + t.append() + + typedef = 'typedef ' if self.name is 'page' else '' + t.append('{}struct css_computed_{}{} {{'.format( + typedef, self.name, '' if self.name is 'page' else '_i')) + + t.comment() + commented = [] + t.table_header() + + for prop in sorted(self.props, key=(lambda x: x.name)): + if prop.comments is None: + t.append(prop.size_line) + else: + commented.extend(( '', prop.comments, '', prop.size_line )) + + t.append(commented) + t.append() + t.table_line() + t.append('{:{len_1}}{:>3}{:{len_2}}{:>3}{}{}'.format('', + str(self.bits_size), ' bits', str(self.bytes_size), + ' + ' + str(self.ptr_size) + 'sizeof(ptr)' + if self.ptr_size else '', + ' bytes', + len_1=Text.name_width, len_2=(Text.bits_width - 3))) + t.result_line() + t.append('{:{len_1}}{:>3}{}{}'.format('', + math.ceil(self.bits_size / 8) + self.bytes_size, + ' + ' + str(self.ptr_size) + 'sizeof(ptr)' + if self.ptr_size else '', + ' bytes', len_1=Text.name_width)) + t.append() + + t.append('Bit allocations:') + for i, b in enumerate(self.bits_array): + bits = [] + for prop in b.contents: + for char in prop.name + prop.name.upper(): + if char not in bits and char in string.ascii_letters: + bits.extend(char * prop.bits_size) + break + t.append() + t.append('{:<2} {:.<32}'.format(str(i), ''.join(bits))) + t.append('; '.join([ p.name for p in b.contents ])) + t.comment() + + t.indent(1) + t.append('uint32_t bits[' + str(len(self.bits_array)) + '];') + t.append() + t.append(self.make_value_declaration(for_commented=False)) + + if self.name is 'style': + t.append() + for g in css_groups: + if g.name is not 'style' and g.name is not 'page': + t.append('css_computed_{0} *{0};'.format(g.name)) + t.append('void *aural;') + + t.indent(-1) + t.append('}}{};'.format( + ' css_computed_' + self.name if typedef else '')) + + if self.name is not 'page': + typedef = 'typedef ' if self.name is not 'style' else '' + t.append() + t.append('{}struct css_computed_{} {{'.format( + typedef, self.name)) + t.indent(1) + t.append('struct css_computed_' + self.name + '_i i;') + t.append() + t.append(self.make_value_declaration(for_commented=True)) + t.append() + + if self.name is 'style': + t.append('css_computed_page *page;') + + t.append('struct css_computed_' + self.name + ' *next;') + t.append('uint32_t count;') + t.append('uint32_t bin;') + t.indent(-1) + t.append('}}{};'.format( + ' css_computed_' + self.name if typedef else '')) + + return t.to_string() + + def make_propset_h(self): + """Output this group's property functions for the propset.h file. + + If group is not `style`, will also output the defaults + and the ENSURE_{group} texts. + """ + t = Text() + i_dot, grp = self.get_idot_grp() + + if self.name is not 'style': + t.append('static const css_computed_{0} default_{0} = {{'.format( + self.name)) + t.indent(1) + + if self.name is not 'page': + t.append('.i = {') + t.indent(1) + + t.append('.bits = {') + t.indent(1) + + bits_ops = [] + for b in self.bits_array: + or_ops = [] + for p in b.contents: + or_ops.append('({} << {})'.format(p.defaults, str(p.shift)) + if p.shift else p.defaults) + bits_ops.append(' | '.join(or_ops)) + + t.append(',\n'.join(bits_ops).split('\n')) + t.indent(-1) + t.append('},') + t.append(',\n'.join( + self.make_value_declaration(False, True)).split('\n')) + + if self.name is not 'page': + t.indent(-1) + t.append('},') + t.append(',\n'.join( + self.make_value_declaration(True, True) + + [ '.next = NULL', '.count = 0', '.bin = UINT32_MAX' ] + ).split('\n')) + + t.indent(-1) + t.append('};') + + t.append() + t.escape_newline() + t.append('#define ENSURE_{} do {{'.format(self.name.upper())) + t.indent(1) + t.append('if (style->{}{} == NULL) {{'.format(i_dot, self.name)) + t.indent(1) + t.append('style->{}{n} = malloc(sizeof(css_computed_{n}));'.format( + i_dot, n=self.name)) + t.append('if (style->{}{} == NULL)'.format(i_dot, self.name)) + t.indent(1) + t.append('return CSS_NOMEM;') + t.indent(-1) + t.append() + t.append('memcpy(style->{}{n}, &default_{n}, ' + 'sizeof(css_computed_{n}));'.format(i_dot, n=self.name)) + t.indent(-1) + t.append('}') + t.indent(-1) + t.append('} while(0)') + t.escape_newline() + t.append() + + for p in sorted(self.props, key=(lambda x: x.name)): + defines, undefs = p.def_undefs + + t.append() + t.append(defines) + + if p.name in overrides['set']: + t.append(overrides['set'][p.name], pre_formatted=True) + t.append(undefs) + continue + + vals = p.get_param_values() + params = ', '.join([ 'css_computed_style *style', 'uint8_t type' ] + + [ ' '.join(x) for x in vals ]) + t.append() + t.append('static inline css_error set_{}({})'.format( + p.name, params)) + t.append('{') + t.indent(1) + + t.append('uint32_t *bits;') + t.append() + + if self.name is not 'style': + t.append('ENSURE_{};'.format(self.name.upper())) + t.append() + + t.append('bits = &style{}->{}bits[{}_INDEX];'.format( + grp, i_dot, p.name.upper())) + t.append() + + type_mask, shift_list, bits_comment = p.get_bits() + t.append(bits_comment) + type_mask = '(type & {})'.format(type_mask) + val_list = [ '({} << {})'.format(x[0], x[1]) for x in shift_list ] + ops_str = ' | '.join([ type_mask ] + val_list) + t.append('*bits = (*bits & ~{0}_MASK) | ' + '({1}{2}{3} << {0}_SHIFT);'.format( + p.name.upper(), + '(' if val_list else '', + ops_str, + ')' if val_list else '')) + + t.append() + for v in p.values: + old_n = 'old_' + v.name + v.suffix + old_t, old_n_shift = shift_star(v.type, old_n) + + if v.name is 'string': + t.append('{} {} = style{}->{}{} = {};'.format( + old_t, old_n_shift, + grp, i_dot, p.name + v.suffix, v.name + v.suffix)) + t.append() + t.append('if ({} != NULL) {{'.format(v.name + v.suffix)) + t.indent(1) + t.append('style{}->{}{} = lwc_string_ref({});'.format( + grp, i_dot, p.name + v.suffix, v.name + v.suffix)) + t.indent(-1) + t.append('} else {') + t.indent(1) + t.append('style{}->{}{} = NULL;'.format( + grp, i_dot, p.name + v.suffix)) + t.indent(-1) + t.append('}') + t.append() + t.append('if ({} != NULL)'.format(old_n)) + t.indent(1) + t.append('lwc_string_unref({});'.format(old_n)) + t.indent(-1) + + elif v.name is 'string_arr' or v.name is 'counter_arr': + iter_var = 's' if v.name is 'string_arr' else 'c' + iter_deref = '*s' if v.name is 'string_arr' else 'c->name' + t.append('{} {} = style{}->{};'.format( + old_t, old_n_shift, + grp, p.name + v.suffix)) + t.append('{} {};'.format(old_t, + shift_star(v.type, iter_var)[1])) + t.append() + t.append('for ({0} = {2}; {0} != NULL && ' + '{1} != NULL; {0}++)'.format(iter_var, iter_deref, + v.name + v.suffix)) + t.indent(1) + t.append('{0} = lwc_string_ref({0});'.format(iter_deref)) + t.indent(-1) + t.append() + t.append('style{}->{} = {};'.format( + grp, p.name + v.suffix, v.name + v.suffix)) + t.append() + t.append('/* Free existing array */') + t.append('if ({} != NULL) {{'.format(old_n)) + t.indent(1) + t.append('for ({0} = {2}; {1} != NULL; {0}++)'.format( + iter_var, iter_deref, old_n)) + t.indent(1) + t.append('lwc_string_unref({});'.format(iter_deref)) + t.indent(-1) + t.append() + t.append('if ({} != {})'.format(old_n, v.name + v.suffix)) + t.indent(1) + t.append('free({});'.format(old_n)) + t.indent(-2) + t.append('}') + + elif not v.is_ptr: + t.append('style{}->{}{} = {};'.format( + grp, i_dot, p.name + v.suffix, v.name + v.suffix)) + + else: + raise ValueError('Cannot handle value ' + v.name +'!') + + t.append() + t.append('return CSS_OK;') + t.indent(-1) + t.append('}') + t.append(undefs) + + return t.to_string() + + def make_propget_h(self): + """Output this group's property functions for the propget.h file.""" + t = Text() + i_dot, grp = self.get_idot_grp() + + for p in sorted(self.props, key=(lambda x: x.name)): + defines, undefs = p.def_undefs + + t.append() + t.append(defines) + + if p.name in overrides['get']: + t.append(overrides['get'][p.name], pre_formatted=True) + t.append(undefs) + continue + + vals = p.get_param_values(pointer=True) + params = ', '.join([ 'css_computed_style *style' ] + + [ ' '.join(x) for x in vals ]) + t.append('static inline uint8_t get_{}(const {})'.format( + p.name, params)) + t.append('{') + t.indent(1) + + if self.name is not 'style': + t.append('if (style{} != NULL) {{'.format(grp)) + t.indent(1) + + t.append('uint32_t bits = style{}->{}bits[{}_INDEX];'.format( + grp, i_dot, p.name.upper())) + t.append('bits &= {}_MASK;'.format(p.name.upper())) + t.append('bits >>= {}_SHIFT;'.format(p.name.upper())) + t.append() + + type_mask, shift_list, bits_comment = p.get_bits() + t.append(bits_comment) + + if p.condition: + t.append('if ((bits & {}) == {}) {{'.format( + type_mask, p.condition)) + t.indent(1) + + for v in p.values: + this_idot = '' if v.is_ptr and v.name != 'string' else i_dot + t.append('*{} = style{}->{}{};'.format( + v.name + v.suffix, grp, this_idot, p.name + v.suffix)) + for i, v in enumerate(list(reversed(shift_list))): + if i is 0: + t.append('*{} = bits >> {};'.format(v[0], v[1])) + else: + t.append('*{} = (bits & 0x{:x}) >> {};'.format( + v[0], v[2], v[1]).lower()) + + if p.condition: + t.indent(-1) + t.append('}') + + t.append() + t.append('return (bits & {});'.format(type_mask)) + + if self.name is not 'style': + t.indent(-1) + t.append('}') + t.append() + t.append('/* Initial value */') + for v in p.values: + t.append('*{} = {};'.format(v.name + v.suffix, v.defaults)) + if v.bits is not None: + t.append('*{} = {};'.format( + v.bits['name'] + v.suffix, v.bits['defaults'])) + t.append('return {};'.format(p.defaults)) + + t.indent(-1) + t.append('}') + t.append(undefs) + + return t.to_string() + + def make_value_declaration(self, for_commented, defaults=False): + """Output declarations of values for this group's properties. + + Args: + for_commented: only parse values that have a `comment` field + defaults: outputs default value assignments. + """ + + r = [] + for p in sorted(self.props, key=(lambda x: x.name)): + if bool(p.comments) == for_commented: + for v in p.values: + if defaults: + r.append('.{}{} = {}'.format(p.name, v.suffix, + v.defaults)) + else: + v_type, v_name = shift_star(v.type, p.name) + r.append('{} {}{};'.format(v_type, v_name, v.suffix)) + return r + + def make_text(self, filename): + """Return this group's text for the given file.""" + if filename == 'computed.h': + return self.make_computed_h() + elif filename == 'propset.h': + return self.make_propset_h() + elif filename == 'propget.h': + return self.make_propget_h() + else: + raise ValueError() + +css_groups = [ CSSGroup(g) for g in groups ] +dir_path = os.path.dirname(os.path.realpath(__file__)) + +for k, v in assets.items(): + # Key is filename string (e.g. "computed.h") without autogenerated_ prefix + body = '\n'.join([ g.make_text(k) for g in css_groups ]) + text = '\n'.join([ v['header'], body, v['footer'] ]) + with open(os.path.join(dir_path, 'autogenerated_') + k, 'w') as file_k: + file_k.write(text) -- cgit v1.2.3