#!/usr/bin/env python # # Copyright (c) 2008-2010, 2013 Wind River Systems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the Lesser GNU General Public License version 2.1 as # published by the Free Software Foundation. # # 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 Lesser GNU General Public License for more details. # # You should have received a copy of the Lesser GNU General Public License # version 2.1 along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """convert tables.in files to enums, tables, and support code. Inputs are a type name, prefix, and a list of columns, followed by a list of names with optional "= value" suffixes, plus optional additional columns. The names are used to create enums and a table of strings, as well as to/from lookups between the ids and names. If additional columns are defined, each column (separated by ", ") is used to create an additional table of the given name, and a lookup function from ids. Example: foo: FFF; const char *bar = "GOZINTA" hello, "yah" world, "nope" produces: typedef enum { FFF_UNKNOWN = -1, FFF_MIN = 0, FFF_NONE = 0, FFF_HELLO, FFF_WORLD, FFF_MAX } foo_id_t; extern const char *foo_name(foo_id_t id); extern foo_id_t foo_id(const char *name); extern const char *foo_bar(foo_id_t id); such that foo_name(1) => "hello" and foo_bar(1) => "yah". If there is an assigned value for a column description, missing column values yield that value, otherwise they yield "unknown". Values out of range yield "unknown", and unrecognized names yield the value -1. Note that the "MAX" value is one more than the highest defined value. (This is for consistency with C array bounds.) """ import glob import sys import string import os from templatefile import TemplateFile class DataType: """a set of related DataItem objects""" def __init__(self, path): """read the first line of path, then make tuples of the rest""" with open(path,'r') as source: definition = source.readline().rstrip() self.name, qualifiers = definition.split(': ', 2) if '; ' in qualifiers: self.prefix, columns = qualifiers.split('; ') else: self.prefix = qualifiers columns = [] self.flags = False if len(columns): self.columns = [] columns = columns.split(', ') for col in columns: indexed = False if col.startswith("FLAGS"): print("Flags: set for %s" % self.name) self.flags = True continue if col.startswith("INDEXED "): col = col[8:] indexed = True if "=" in col: name, default = col.split(' = ') else: name, default = col, "" if " " in name: words = name.split(' ') name = words[-1] del words[-1] type = ' '.join(words) else: type = "char *" self.columns.append({"indexed":indexed, "type":type, "name":name, "value":default}) else: self.columns = [] self.data = [] self.comments = [] index = 1 for line in source.readlines(): item = {} if line.startswith('#'): self.comments.append(line.rstrip().replace('#', '')) continue # first entry on the line is the "real" name/id, following hunks # are additional columns cols = line.rstrip().split(', ') item["name"] = cols.pop(0) item["upper"] = item["name"].replace('-', '_').upper() column_list = [] for col in self.columns: if len(cols) > 0: value = cols.pop(0) if col["indexed"]: if not "max" in col: col["max"] = value if value > col["max"]: col["max"] = value if not "min" in col: col["min"] = value if value < col["min"]: col["min"] = value column_list.append({"name":col["name"], "value":value}) else: column_list.append({"name":col["name"], "value":col["value"]}) item["cols"] = column_list item["index"] = index index = index + 1 self.data.append(item) def __getitem__(self, key): """Make this object look like a dict for Templates to use""" attr = getattr(self, key) if callable(attr): return attr() else: return attr def __repr__(self): column = 0 out = "" out += "type: %s_t" % self.name out += " (prefix '%s_ENUM')\n" % self.prefix for col in self.columns: out += " extra column: %s %s (default %s)\n" % (col["type"], col["name"], col["value"]) out += " " for item in self.data: column = column + 1 if column > 4 and column % 4 == 1: out += "\n " out += "%-19s" % item["name"] # for col in item["cols"]: # out += "\t%s(%s)\n" % (col["name"], col["value"]) return out def comment(self): if len(self.comments): return '/*' + '\n *'.join(self.comments) + ' */\n' else: return '' def names(self): return ',\n\t'.join('"%s"' % x["name"] for x in self.data) def enums(self): return ',\n\t'.join('%s_%s' % (self.prefix, x["upper"]) for x in self.data) def flag_enums(self): if not self.flags: return "" enum_lines = [] enum_lines.append('typedef enum {') prefix = self.prefix + 'F' for x in self.data: enum_lines.append('\t%s_%s = (1 << %s_%s),' % (prefix, x["upper"], self.prefix, x["upper"])) enum_lines.append('} pseudo_%s_f;' % self.name) return '\n'.join(enum_lines) def column_names(self): decl_lines = [] column = 0 for col in self.columns: decl_lines.append("static %s %s_id_to_%s[] = {" % (col["type"], self.name, col["name"])) decl_lines.append('\t%s,' % col["value"]) for item in self.data: decl_lines.append('\t%s,' % item["cols"][column]["value"]) decl_lines.append('\t0') decl_lines.append("};") if col["indexed"]: decl_lines.append("static int %s_%s_to_id[] = {" % (self.name, col["name"])) for item in self.data: decl_lines.append('\t[%s] = %d,' % (item["cols"][column]["value"], item["index"])) decl_lines.append("};") column = column + 1 return '\n'.join(decl_lines) def column_funcs(self): decl_lines = [] for col in self.columns: decl_lines.append('extern %s' % col["type"]) decl_lines.append('pseudo_%s_%s(pseudo_%s_t id) {' % (self.name, col["name"], self.name)) decl_lines.append('\tif (id < 0 || id >= %s_MAX)' % (self.prefix)) decl_lines.append('\t\treturn %s;' % col["value"]) decl_lines.append('\treturn %s_id_to_%s[id];' % (self.name, col["name"])) decl_lines.append('}') if col["indexed"]: table_name = '%s_%s_to_id' % (self.name, col["name"]) decl_lines.append('extern int') decl_lines.append('pseudo_%s_%s_id(%s val) {' % (self.name, col["name"], col["type"])) decl_lines.append('\tif ((val < %s) || (val > %s)) {' % (col["min"], col["max"])) decl_lines.append('\t\treturn -1;') decl_lines.append('\t}') decl_lines.append('\tif (%s[val] != 0) {' % table_name) decl_lines.append('\t\treturn %s[val];' % table_name) decl_lines.append('\t}') decl_lines.append('\treturn -1;') decl_lines.append('}') return '\n'.join(decl_lines) def column_protos(self): decl_lines = [] for col in self.columns: decl_lines.append('extern %s pseudo_%s_%s(pseudo_%s_t id);' % (col["type"], self.name, col["name"], self.name)) if col["indexed"]: decl_lines.append('extern int pseudo_%s_%s_id(%s val);' % (self.name, col["name"], col["type"])) return '\n'.join(decl_lines) def main(): """Read in function defintions, write out files based on templates.""" datatypes = [] templates = [] # error checking helpfully provided by the exception handler copyright_file = open('guts/COPYRIGHT') TemplateFile.copyright = copyright_file.read() copyright_file.close() for path in glob.glob('table_templates/*'): try: template_file = TemplateFile(path) template_file.emit('copyright') template_file.emit('header') templates.append(template_file) except IOError: print("Invalid or malformed template %s. Aborting." % path) exit(1) for filename in sys.argv[1:]: # read in the datatype sys.stdout.write("%s: " % filename) datatype = DataType(filename) datatypes.append(datatype) print(datatype.__repr__()) print("") print("Writing datatypes...") for datatype in datatypes: # populate various tables and files with each datatype for template_file in templates: template_file.emit('body', datatype) print("done. Cleaning up.") for template_file in templates: # clean up files template_file.emit('footer') template_file.close() if __name__ == '__main__': main()