#!/usr/bin/env python
#
# Copyright (c) 2008-2010, 2013 Wind River Systems, Inc.
#
# SPDX-License-Identifier: LGPL-2.1-only
#
"""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()