mirror of
https://github.com/RPCSX/xed.git
synced 2026-01-31 01:05:17 +01:00
Change-Id: I166833daaa56c33eca01bdf7b9aa6e74a490ba9a (cherry picked from commit 1212ba962dff6dfbfa0bd2469327ff447ce59058)
1039 lines
34 KiB
Python
1039 lines
34 KiB
Python
#!/usr/bin/env python
|
|
# -*- python -*-
|
|
# Mark Charney <mark.charney@intel.com>
|
|
# Code generation support: emitting files, emitting functions, etc.
|
|
#BEGIN_LEGAL
|
|
#
|
|
#Copyright (c) 2017 Intel Corporation
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
#END_LEGAL
|
|
|
|
import sys
|
|
import os
|
|
import re
|
|
import types
|
|
import glob
|
|
|
|
from genutil import *
|
|
def find_dir(d):
|
|
dir = os.getcwd()
|
|
last = ''
|
|
while dir != last:
|
|
target_dir = os.path.join(dir,d)
|
|
#print "Trying %s" % (target_dir)
|
|
if os.path.exists(target_dir):
|
|
return target_dir
|
|
last = dir
|
|
(dir,tail) = os.path.split(dir)
|
|
return None
|
|
sys.path.append(find_dir('mbuild'))
|
|
try:
|
|
import mbuild
|
|
except:
|
|
sys.stderr.write("\nERROR(file: codegen.py): Could not find mbuild. Might try setting PYTHONPATH env var.\n\n")
|
|
sys.exit(1)
|
|
|
|
class ip_header_t(object):
|
|
"""Intellectual property headers"""
|
|
def __init__(self):
|
|
self.lines = None
|
|
def read_header(self,fn):
|
|
##msge("Attempting to open: " + fn)
|
|
fp = base_open_file(fn,"r")
|
|
self.lines = fp.readlines()
|
|
|
|
def emit_header(self, shell_type = False):
|
|
eol = '\n'
|
|
out = []
|
|
if shell_type:
|
|
out.append("#BEGIN_LEGAL" + eol)
|
|
for line in self.lines:
|
|
out.append("#" + line)
|
|
out.append("#END_LEGAL" + eol)
|
|
|
|
else:
|
|
out.append("/*BEGIN_LEGAL" + eol)
|
|
out.extend(self.lines)
|
|
out.append("END_LEGAL */" + eol)
|
|
return out
|
|
|
|
|
|
class file_emitter_t(object):
|
|
"""Attach IP headers, standard includes, and namespace decorations
|
|
to generated files. This replaces the file objects I was using for
|
|
emitting files."""
|
|
header_file_name_pattern = re.compile(r'.[hH]$')
|
|
|
|
|
|
# note: in the following the '-' must be last or it will (try to) act like a range!
|
|
header_guard_pattern = re.compile(r'[./-]')
|
|
|
|
def __init__(self,gendir, file_name, shell_file=False, namespace=None):
|
|
"""gendir is the output dir. If shell_file is True, we delimit
|
|
the header differently."""
|
|
self.file_name = file_name
|
|
|
|
self.gendir = gendir
|
|
self.namespace = namespace
|
|
# True for shell-like files, False for C++ files. Determines the comment syntax
|
|
self.shell_file = shell_file
|
|
|
|
self.lines = []
|
|
self.full_file_name = mbuild.join(self.gendir, self.file_name)
|
|
self.eol = '\n'
|
|
self.closed = False
|
|
self.header = False
|
|
if file_emitter_t.header_file_name_pattern.search(self.file_name):
|
|
self.header = True
|
|
self.headers = []
|
|
self.system_headers = []
|
|
self.misc_header = []
|
|
|
|
|
|
def add_header(self,h):
|
|
"""Add h to the list of headers"""
|
|
if type(h) == list:
|
|
self.headers.extend(h)
|
|
else:
|
|
self.headers.append(h)
|
|
|
|
def add_system_header(self,h):
|
|
"""Add h to the list of system headers"""
|
|
if type(h) == list:
|
|
self.system_headers.extend(h)
|
|
else:
|
|
self.system_headers.append(h)
|
|
|
|
def add_misc_header(self,h):
|
|
if type(h) == list:
|
|
self.misc_header.extend(h)
|
|
else:
|
|
self.misc_header.append(h)
|
|
|
|
def replace_headers(self,h):
|
|
"""Replace the existing headers with the header h"""
|
|
if type(h) == list:
|
|
self.headers = h
|
|
else:
|
|
self.headers = [h]
|
|
|
|
def start(self, full_header=True):
|
|
"""Call this this after creating the objectd"""
|
|
self.emit_header(full_header)
|
|
if not self.shell_file:
|
|
self.system_headers_emit()
|
|
self.user_headers_emit()
|
|
self.misc_headers_emit()
|
|
self.namespace_start()
|
|
|
|
def count_lines(self):
|
|
return len(self.lines)
|
|
|
|
def write(self,str):
|
|
"""Replaces the file pointer write() function call"""
|
|
self.lines.append(str)
|
|
def writelines(self,list_of_str):
|
|
"""Replaces the file pointer writelines() function call"""
|
|
self.lines.extend(list_of_str)
|
|
def add_code(self,str):
|
|
"""Add a line and newline"""
|
|
self.write(str+'\n')
|
|
def add_code_eol(self,str):
|
|
"""Add a line with semicolon, newline"""
|
|
self.add_code(str+';')
|
|
|
|
def close(self):
|
|
if not self.closed:
|
|
self.closed = True
|
|
if not self.shell_file:
|
|
self.namespace_end()
|
|
if self.header:
|
|
self.emit_header_guard_end()
|
|
self.emit_file()
|
|
del self.lines
|
|
else:
|
|
msge("FE: Closing an already-closed file: " + self.full_file_name)
|
|
|
|
def emit_file(self):
|
|
msge("FE:EMIT_FILE " + self.full_file_name)
|
|
fp = self.open_file(self.full_file_name,"w")
|
|
fp.writelines(self.lines)
|
|
fp.close()
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
def open_file(self,fn,rw):
|
|
fp = base_open_file(fn,rw)
|
|
return fp
|
|
|
|
def emit_ip_header(self, ip_header_file_name):
|
|
iph = ip_header_t()
|
|
iph.read_header(ip_header_file_name)
|
|
s = iph.emit_header(self.shell_file)
|
|
return s
|
|
|
|
def emit_header(self, full_header=True):
|
|
if full_header:
|
|
self.dox('@file ' + self.file_name)
|
|
self.emit()
|
|
self.cmt('This file was automatically generated.')
|
|
self.cmt('Do not edit this file.')
|
|
self.emit()
|
|
if self.header:
|
|
self.emit_header_guard_start()
|
|
|
|
def emit_header_guard_start(self):
|
|
s = file_emitter_t.header_guard_pattern.sub('_',self.file_name)
|
|
defname = '%s' % s.upper()
|
|
self.emit_eol('#if !defined(' + defname + ')')
|
|
self.emit_eol('# define ' + defname )
|
|
def emit_header_guard_end(self):
|
|
self.emit_eol("#endif")
|
|
|
|
def dox(self,s):
|
|
if self.shell_file:
|
|
self.emit_eol('# '+ s)
|
|
else:
|
|
self.emit_eol('/// '+ s)
|
|
def cmt(self,s):
|
|
if self.shell_file:
|
|
self.emit_eol('# '+ s)
|
|
else:
|
|
self.emit_eol('// '+ s)
|
|
def emit(self,s='\n'):
|
|
self.lines.append(s)
|
|
def emit_eol(self,s=''):
|
|
self.emit(s + '\n')
|
|
|
|
def user_headers_emit(self):
|
|
for h in self.headers:
|
|
self.emit_eol('#include \"%s\"' % h )
|
|
|
|
def system_headers_emit(self):
|
|
for h in self.system_headers:
|
|
self.emit_eol('#include <%s>' % h )
|
|
|
|
def misc_headers_emit(self):
|
|
for h in self.misc_header:
|
|
self.emit_eol(h)
|
|
|
|
|
|
def namespace_start(self):
|
|
if self.namespace:
|
|
self.emit_eol( ''.join(['namespace ' , self.namespace , ' {']))
|
|
def namespace_end(self):
|
|
if self.namespace:
|
|
msge("FE:NAMESPACE " + self.full_file_name)
|
|
self.emit_eol( '} // namespace')
|
|
|
|
|
|
|
|
|
|
class xed_file_emitter_t(file_emitter_t):
|
|
"""Attach IP headers, standard includes, and namespace decorations
|
|
to generated files. This replaces the file objects I was using for
|
|
emitting files."""
|
|
|
|
def __init__(self, xeddir, gendir, file_name, shell_file=False,
|
|
namespace=None, is_private=True):
|
|
file_emitter_t.__init__( self,gendir, file_name, shell_file, namespace)
|
|
self.xeddir = xeddir
|
|
if is_private:
|
|
self.headers.append('xed-internal-header.h')
|
|
|
|
def start(self, full_header=True):
|
|
"""override the parent's start() function to apply the IP
|
|
header."""
|
|
self.emit_header(full_header)
|
|
ip_header_file_name = mbuild.join(self.xeddir,
|
|
'misc',
|
|
'apache-header.txt')
|
|
for line in self.emit_ip_header(ip_header_file_name):
|
|
self.emit(line)
|
|
if not self.shell_file:
|
|
self.system_headers_emit()
|
|
self.user_headers_emit()
|
|
self.misc_headers_emit()
|
|
self.namespace_start()
|
|
|
|
#inline_string = "inline"
|
|
inline_sring = "XED_INLINE"
|
|
|
|
|
|
class function_object_t(object):
|
|
inline_string = "XED_INLINE"
|
|
|
|
def __init__(self,name, return_type='xed_bool_t', static=False, inline=False,
|
|
doxgroup=None, force_no_inline=False, dll_export=False):
|
|
self.function_name = name
|
|
self.doxgroup=doxgroup
|
|
self.return_type = return_type
|
|
self.static=static
|
|
self.inline=inline
|
|
self.dll_export = dll_export
|
|
self.body = []
|
|
self.args = []
|
|
self.const_member = False
|
|
self.ref_return = False
|
|
self.force_no_inline = force_no_inline
|
|
|
|
def set_function_name(self,fname):
|
|
self.function_name = fname
|
|
|
|
def lines(self):
|
|
return len(self.body)
|
|
|
|
def add_arg(self, arg):
|
|
self.args.append(arg)
|
|
|
|
def get_arg_num(self):
|
|
return len(self.args)
|
|
|
|
def add_comment(self,s):
|
|
self.body.append(''.join(['/* ' , s , ' */']))
|
|
|
|
def set_const_member(self):
|
|
self.const_member = True
|
|
def set_ref_return(self):
|
|
self.ref_return = True
|
|
|
|
def add_code(self, line):
|
|
self.body.append(line)
|
|
def add_code_eol(self, line):
|
|
self.body.append(line + ';')
|
|
|
|
def add_lines(self, lines):
|
|
self.body.extend(lines)
|
|
|
|
def emit_header_internal(self, class_qualfier='', emit_group=False):
|
|
"""private function that emits the function name and args, but
|
|
no newline or semicolon"""
|
|
s = []
|
|
if emit_group and self.doxgroup:
|
|
s.append("/// @ingroup %s\n" % (self.doxgroup))
|
|
if self.static:
|
|
s.append("static ")
|
|
if self.inline:
|
|
s.append( function_object_t.inline_string + " ")
|
|
if self.force_no_inline:
|
|
s.append('XED_NOINLINE ')
|
|
if self.dll_export:
|
|
s.append('XED_DLL_EXPORT ')
|
|
s.append(self.return_type)
|
|
if self.ref_return:
|
|
s.append('&')
|
|
s.append(' ')
|
|
|
|
s.append(class_qualfier)
|
|
|
|
s.append(self.function_name)
|
|
s.append('(')
|
|
first_arg = True
|
|
for arg in self.args:
|
|
if first_arg:
|
|
first_arg = False
|
|
else:
|
|
s.append(', ')
|
|
s.append(arg)
|
|
if first_arg:
|
|
s.append('void')
|
|
s.append(')')
|
|
if self.const_member:
|
|
s.append(' const')
|
|
return ''.join(s)
|
|
|
|
def emit_header(self):
|
|
'emit the header with the semicolon and newline'
|
|
s = [ self.emit_header_internal(emit_group=True), ";\n" ]
|
|
return ''.join(s)
|
|
|
|
def emit(self, class_qualfier=''):
|
|
'emit the function body'
|
|
eol = '\n'
|
|
s = [ self.emit_header_internal(class_qualfier) , eol ]
|
|
s.append('{')
|
|
s.append(eol)
|
|
for bline in self.body:
|
|
s.extend([ ' ' , bline , eol])
|
|
s.append('}')
|
|
s.append(eol)
|
|
return ''.join(s)
|
|
|
|
def emit_file_emitter(self, fe, class_qualfier=''):
|
|
'emit the function body'
|
|
fe.add_code(self.emit_header_internal(class_qualfier))
|
|
fe.add_code('{')
|
|
for bline in self.body:
|
|
fe.add_code(bline)
|
|
fe.add_code('}')
|
|
|
|
def emit_body(self):
|
|
'emit function body as string'
|
|
return '\n'.join([bline + ';' for bline in self.body])
|
|
|
|
############################################################
|
|
|
|
def dump_flist_2_header(h_file, functions, headers,
|
|
emit_headers=True,
|
|
emit_bodies=True):
|
|
''' emits the list of functions objects to a header file
|
|
@type: functions: list of function_object_t
|
|
@param functions: the function to emit
|
|
@type: h_file: xed_file_emitter_t
|
|
@param h_file: emmiting the function to this headr file
|
|
@type: headers: list of strings
|
|
@param headers: include headers to emit
|
|
'''
|
|
for header in headers:
|
|
h_file.add_header(header)
|
|
h_file.start()
|
|
|
|
if emit_headers:
|
|
for fo in functions:
|
|
decl = fo.emit_header()
|
|
h_file.add_code(decl)
|
|
|
|
if emit_bodies:
|
|
for fo in functions:
|
|
fo.emit_file_emitter(h_file)
|
|
|
|
h_file.close()
|
|
|
|
def emit_function_list(func_list, fn_prefix, xeddir, gendir, hgendir, namespace=None, other_headers=[]):
|
|
"""Emit a list of functions to a numbered sequence of
|
|
files. Breaking them up when the files get too big.
|
|
|
|
@type func_list: list of function_object_t objects
|
|
@param func_list: functions to emit
|
|
@type fn_prefix: string
|
|
@param fn_prefix: basis for the output file names.
|
|
@type xeddir: string
|
|
@param xeddir: location of the source directory so that we can find the legal header
|
|
@type gendir: string
|
|
@param gendir: directory where the output files go.
|
|
@type hgendir: string
|
|
@param hgendir: directory where the output hdr files go.
|
|
@type namespace: string
|
|
@param namespace: defaults to XED
|
|
"""
|
|
file_number = 0;
|
|
max_lines_per_file = 3000
|
|
fe = None
|
|
fn_header = "%s.h" % (fn_prefix)
|
|
fe_list = []
|
|
fe_header = xed_file_emitter_t(xeddir,hgendir,fn_header,shell_file=False,namespace=namespace)
|
|
fe_header.start()
|
|
fe_list.append(fe_header)
|
|
|
|
# remove any numbered files that we previously emitted. We won't
|
|
# necessarily overwrite them all each build and do not want
|
|
# stale files remaining from previous builds
|
|
for fn in glob.glob(mbuild.join(gendir, fn_prefix + '-[0-9]*.c')):
|
|
mbuild.remove_file(fn)
|
|
|
|
for func in func_list:
|
|
fe_header.write(func.emit_header())
|
|
if not fe or fe.count_lines() + func.lines() >= max_lines_per_file:
|
|
if fe:
|
|
fe.close()
|
|
fn = "%s-%d.c" % (fn_prefix, file_number)
|
|
fe = xed_file_emitter_t(xeddir,gendir, fn, shell_file=False, namespace=namespace)
|
|
fe.add_header(fn_header)
|
|
for header in other_headers:
|
|
fe.add_header(header)
|
|
fe.start()
|
|
fe_list.append(fe)
|
|
file_number += 1
|
|
|
|
func.emit_file_emitter(fe)
|
|
|
|
|
|
fe.close()
|
|
fe_header.close()
|
|
return fe_list
|
|
|
|
|
|
############################################################
|
|
|
|
|
|
def function_call_sequence(fname, lst):
|
|
"""Return a function object (returning nothing) for a function
|
|
named fname that calls all the functions in lst.
|
|
|
|
@type fname: string
|
|
@param fname: function name
|
|
@type lst: list
|
|
@param lst: list of function names without parens
|
|
@rtype: function_object_t
|
|
@return: function that calls each function in lst
|
|
"""
|
|
fo = function_object_t(fname, "void")
|
|
for fn in lst:
|
|
fo.add_code_eol(fn + "()")
|
|
return fo
|
|
|
|
def function_call_sequence_conditional(fname, lst, subroutine_arg=''):
|
|
"""Return a function object (that returns xed_bool_t) for a function named fname that calls
|
|
all the functions in lst. Check each function call for an okay
|
|
return value and have this function return false if any of the
|
|
subroutines return false.
|
|
|
|
@type fname: string
|
|
@param fname: function name
|
|
@type lst: list
|
|
@param lst: list of function names without parens
|
|
@type subroutine_arg: string
|
|
@param subroutine_arg: optional parameter for the called functions
|
|
|
|
@rtype: function_object_t
|
|
@return: function that calls each function in lst
|
|
"""
|
|
fo = function_object_t(fname, "xed_bool_t")
|
|
fo.add_code_eol("xed_bool_t okay")
|
|
args = "(%s)" % subroutine_arg
|
|
for fn in lst:
|
|
fo.add_code_eol("okay = " + fn + args)
|
|
fo.add_code_eol("if (!okay) return 0")
|
|
fo.add_code_eol("return 1")
|
|
return fo
|
|
|
|
class class_generator_t(object):
|
|
"""Generate code for a c++ class (or union) declaration and
|
|
implementation.
|
|
|
|
If you want initialization or a printer, you can add your create
|
|
your own functions and add them with add_function().
|
|
"""
|
|
inline_string = "XED_INLINE"
|
|
inline_pattern = re.compile(inline_string)
|
|
|
|
def __init__(self,name, class_or_union='class', var_prefix = "_"):
|
|
self.name = name
|
|
self.var_type = [] # list of (variable,type,bit_width) tuples
|
|
self.functions = [] # member functions
|
|
self.class_or_union = class_or_union
|
|
self.var_prefix = var_prefix
|
|
|
|
def add_var(self, var, type, bit_width = None, accessors='set-get'):
|
|
"""Add a variable var of type. If accessors is set, generate
|
|
set/get functions for it. The potential values are the following
|
|
strings:
|
|
|
|
set
|
|
get
|
|
set-get
|
|
get-ref
|
|
set-get-array
|
|
none
|
|
"""
|
|
pvar = self.var_prefix + var
|
|
self.var_type.append((pvar,type,bit_width))
|
|
if accessors == 'set-get-array':
|
|
self.add_function(self.add_get_array_fn(var,pvar,type))
|
|
self.add_function(self.add_set_array_fn(var,pvar,type))
|
|
if accessors == 'set-get':
|
|
self.add_function(self.add_get_fn(var,pvar,type))
|
|
self.add_function(self.add_set_fn(var,pvar,type))
|
|
elif accessors == 'set':
|
|
self.add_function(self.add_set_fn(var,pvar,type))
|
|
elif accessors == 'get':
|
|
self.add_function(self.add_get_fn(var,pvar,type))
|
|
elif accessors == 'get-ref':
|
|
self.add_function(self.add_get_ref_fn(var,pvar,type))
|
|
elif accessors == 'none':
|
|
pass
|
|
else:
|
|
die("Unhandled accessor keyword: " + accessors)
|
|
|
|
def add_get_ref_fn(self,var,pvar,type):
|
|
'A get-accessor function for class variable pvar, returns a reference'
|
|
fname = 'get_' + var
|
|
fo = function_object_t(fname, inline_string + ' ' + type)
|
|
fo.set_ref_return()
|
|
fo.add_code_eol( 'return %s' %( pvar ))
|
|
return fo
|
|
|
|
def add_get_fn(self,var,pvar,type):
|
|
'A get-accessor function for class variable pvar'
|
|
fname = 'get_' + var
|
|
fo = function_object_t(fname, inline_string + ' ' + type)
|
|
fo.set_const_member()
|
|
fo.add_code_eol( 'return %s' % ( pvar ))
|
|
return fo
|
|
|
|
def add_set_fn(self, var,pvar,type):
|
|
'A set-accessor function for class variable pvar'
|
|
fname = 'set_' + var
|
|
fo = function_object_t(fname, inline_string + ' void')
|
|
fo.add_arg(type + ' arg_' + var)
|
|
fo.add_code_eol( '%s=arg_%s' % (pvar,var))
|
|
return fo
|
|
|
|
def add_get_array_fn(self,var,pvar,type):
|
|
'A get-accessor function for class variable pvar'
|
|
fname = 'get_' + var
|
|
fo = function_object_t(fname, inline_string + ' ' + type)
|
|
fo.add_arg("unsigned int idx") #FIXME: parameterize unsigned int
|
|
fo.set_const_member()
|
|
# FIXME: add bound checking for array index
|
|
fo.add_code_eol( 'return %s[idx]' % (pvar))
|
|
return fo
|
|
|
|
def add_set_array_fn(self, var,pvar,type):
|
|
'A set-accessor function for class variable pvar'
|
|
fname = 'set_' + var
|
|
fo = function_object_t(fname, inline_string +' void')
|
|
fo.add_arg("unsigned int idx") #FIXME: parameterize unsigned int
|
|
fo.add_arg(type + ' arg_' + var)
|
|
# FIXME add bounds checking for array index
|
|
fo.add_code_eol( '%s[idx]=arg_%s' % (pvar, var))
|
|
return fo
|
|
|
|
def add_function(self, function):
|
|
self.functions.append(function)
|
|
|
|
def emit_decl(self):
|
|
'emit the class declaration'
|
|
eol = '\n'
|
|
pad = ' '
|
|
|
|
s = []
|
|
s.append(self.class_or_union + ' ' + self.name + eol)
|
|
s.append('{' + eol)
|
|
s.append(' public:' + eol)
|
|
for (var,type,bit_width) in self.var_type:
|
|
t = pad + type + ' ' + var
|
|
if bit_width:
|
|
t += ' : ' + str(bit_width)
|
|
s.append(t+ ';' + eol)
|
|
for fo in self.functions:
|
|
s.append( pad + fo.emit_header())
|
|
s.append('};' + eol)
|
|
|
|
# emit the inline functions in the header
|
|
for fo in self.functions:
|
|
if class_generator_t.inline_pattern.search(fo.return_type):
|
|
s.append(fo.emit(self.name + '::') )
|
|
|
|
return ''.join(s)
|
|
|
|
def emit_impl(self):
|
|
'emit the class implementation'
|
|
s = []
|
|
# only emit the noninline functions
|
|
for fo in self.functions:
|
|
if not class_generator_t.inline_pattern.search(fo.return_type):
|
|
s.append(fo.emit(self.name + '::') )
|
|
return ''.join(s)
|
|
|
|
############################################################################
|
|
class c_switch_generator_t(object):
|
|
def __init__(self, var_name, func_obj, pad=' '):
|
|
self.func_obj = func_obj
|
|
self.var_name = var_name
|
|
self.pad = pad
|
|
self._add('switch(%s) {' % (self.var_name))
|
|
|
|
def _add(self,s):
|
|
self.func_obj.add_code(self.pad + s)
|
|
|
|
def add_case(self,case_name,clines, do_break=True):
|
|
"""Add a case with a bunch of lines of code -- no semicolons
|
|
required"""
|
|
self._add('case %s:' % (case_name))
|
|
for line in clines:
|
|
self._add(' ' + line)
|
|
if do_break:
|
|
self._add(' break;')
|
|
|
|
def add_default(self,clines, do_break=True):
|
|
"""Add a default case with a bunch of lines of code -- no
|
|
semicolons required"""
|
|
self._add('default:')
|
|
for line in clines:
|
|
self._add(' ' + line)
|
|
if do_break:
|
|
self._add(' break;')
|
|
|
|
def finish(self):
|
|
self._add('}')
|
|
|
|
|
|
############################################################################
|
|
|
|
class c_class_generator_t(object):
|
|
"""Generate code for a C struct (or union) declaration and
|
|
implementation.
|
|
|
|
If you want initialization or a printer, you can add your create
|
|
your own functions and add them with add_function().
|
|
"""
|
|
|
|
type_ending_pattern = re.compile(r'_t$')
|
|
def remove_suffix(self,x):
|
|
return c_class_generator_t.type_ending_pattern.sub('',x)
|
|
|
|
inline_string = "XED_INLINE"
|
|
inline_pattern = re.compile(inline_string)
|
|
|
|
def __init__(self,name, class_or_union='struct', var_prefix = "_"):
|
|
self.name = name
|
|
self.var_type = [] # list of (variable,type,bit_width) tuples
|
|
self.array_type = [] # list of (variable,type,limit) tuples
|
|
self.functions = [] # member functions
|
|
self.class_or_union = class_or_union
|
|
self.var_prefix = var_prefix
|
|
|
|
def add_array(self, var, type, limit):
|
|
"""Add an array variable var of type.
|
|
"""
|
|
pvar = self.var_prefix + var
|
|
self.array_type.append((pvar,type,limit))
|
|
self.add_function(self.add_get_array_fn(var,pvar,type))
|
|
self.add_function(self.add_set_array_fn(var,pvar,type))
|
|
|
|
def add_var(self, var, type, bit_width = None, accessors='set-get'):
|
|
"""Add a variable var of type. If accessors is set, generate
|
|
set/get functions for it. The potential values are the following
|
|
strings:
|
|
|
|
set
|
|
get
|
|
set-get
|
|
get-ref
|
|
set-get-array
|
|
none
|
|
"""
|
|
pvar = self.var_prefix + var
|
|
self.var_type.append((pvar,type,bit_width))
|
|
if accessors == 'set-get-array':
|
|
self.add_function(self.add_get_array_fn(var,pvar,type))
|
|
self.add_function(self.add_set_array_fn(var,pvar,type))
|
|
if accessors == 'set-get':
|
|
self.add_function(self.add_get_fn(var,pvar,type))
|
|
self.add_function(self.add_set_fn(var,pvar,type))
|
|
elif accessors == 'set':
|
|
self.add_function(self.add_set_fn(var,pvar,type))
|
|
elif accessors == 'get':
|
|
self.add_function(self.add_get_fn(var,pvar,type))
|
|
elif accessors == 'get-ref':
|
|
self.add_function(self.add_get_ref_fn(var,pvar,type))
|
|
elif accessors == 'none':
|
|
pass
|
|
else:
|
|
die("Unhandled accessor keyword: " + accessors)
|
|
|
|
def add_get_ref_fn(self,var,pvar,type):
|
|
"""A get-accessor function for class variable pvar, returns a POINTER"""
|
|
fname = self.remove_suffix(self.name) + '_get_' + var
|
|
fo = function_object_t(fname, type + "*")
|
|
fo.add_arg("%s* ppp" % self.name)
|
|
fo.add_code_eol( 'return &(ppp->%s)' %( pvar ))
|
|
return fo
|
|
|
|
def add_get_fn(self,var,pvar,type):
|
|
'A get-accessor function for class variable pvar'
|
|
fname = self.remove_suffix(self.name) + '_get_' + var
|
|
fo = function_object_t(fname, type)
|
|
fo.add_arg("%s* ppp" % self.name)
|
|
fo.add_code_eol( 'return ppp->%s' % ( pvar ))
|
|
return fo
|
|
|
|
def add_set_fn(self, var,pvar,type):
|
|
'A set-accessor function for class variable pvar'
|
|
fname = self.remove_suffix(self.name) + '_set_' + var
|
|
fo = function_object_t(fname, 'void')
|
|
fo.add_arg("%s* ppp" % self.name)
|
|
fo.add_arg(type + ' arg_' + var)
|
|
fo.add_code_eol( 'ppp->%s=arg_%s' % (pvar,var))
|
|
return fo
|
|
|
|
def add_get_array_fn(self,var,pvar,type):
|
|
'A get-accessor function for class variable pvar'
|
|
fname = self.remove_suffix(self.name) + '_get_' + var
|
|
fo = function_object_t(fname, type)
|
|
fo.add_arg("%s* ppp" % self.name)
|
|
fo.add_arg("unsigned int idx") #FIXME: parameterize unsigned int
|
|
# FIXME: add bound checking for array index
|
|
fo.add_code_eol( 'return ppp->%s[idx]' % (pvar))
|
|
return fo
|
|
|
|
def add_set_array_fn(self, var,pvar,type):
|
|
'A set-accessor function for class variable pvar'
|
|
fname = self.remove_suffix(self.name) + '_set_' + var
|
|
fo = function_object_t(fname, 'void')
|
|
fo.add_arg("%s* ppp" % self.name)
|
|
fo.add_arg("unsigned int idx") #FIXME: parameterize unsigned int
|
|
fo.add_arg(type + ' arg_' + var)
|
|
# FIXME add bounds checking for array index
|
|
fo.add_code_eol( 'ppp->%s[idx]=arg_%s' % (pvar, var))
|
|
return fo
|
|
|
|
def add_function(self, function):
|
|
self.functions.append(function)
|
|
|
|
def emit_decl(self):
|
|
'emit the class declaration'
|
|
eol = '\n'
|
|
pad = ' '
|
|
|
|
s = []
|
|
# I replace the suffix _t$ first then append a _s or _u. This way it
|
|
# adds a _s/_u even if no _t is present.
|
|
|
|
struct_name = re.sub(r'_t$', '',self.name )
|
|
if self.class_or_union == 'union':
|
|
struct_name += '_u'
|
|
else:
|
|
struct_name += '_s'
|
|
|
|
s.append('typedef %s %s {\n' % (self.class_or_union, struct_name))
|
|
|
|
for (var,type,limit) in self.array_type:
|
|
t = "%s %s %s[%s];\n" % ( pad, type, var, str(limit))
|
|
s.append(t)
|
|
|
|
for (var,type,bit_width) in self.var_type:
|
|
t = '%s %s %s' % ( pad, type, var)
|
|
if bit_width:
|
|
t += ' : ' + str(bit_width)
|
|
s.append(t+ ';' + eol)
|
|
s.append('} %s;\n' % self.name )
|
|
|
|
# accessor function prototypes
|
|
for fo in self.functions:
|
|
s.append( pad + fo.emit_header())
|
|
return ''.join(s)
|
|
|
|
def emit_impl(self):
|
|
"""emit the class implementation"""
|
|
s = []
|
|
# only emit the noninline functions
|
|
for fo in self.functions:
|
|
s.append(fo.emit() )
|
|
return ''.join(s)
|
|
|
|
|
|
############################################################################
|
|
|
|
class array_gen_t(object):
|
|
"""A simple C++ multidimensional array generator. The ranges are
|
|
typed. New values are added by specifying a list of indices, one
|
|
per dimension and a value"""
|
|
def __init__(self, name, type, target_op=None):
|
|
"""Set the name and storage type for the array"""
|
|
self.name = name
|
|
self.type = type
|
|
self.target_op = target_op
|
|
|
|
self.ranges = [] # list of tuples (range_type, minval, maxval+1, argname )
|
|
# including the max
|
|
# value for dimensioning the array
|
|
|
|
self.values = [] # list of tuples of tuples (dict(names->indices), value)
|
|
|
|
self.lookup_fn = None
|
|
self.init_fn = None
|
|
|
|
def add_dimension(self, range_type, range_min, range_max, argname):
|
|
"""For one dimension, add the type of the range index and the
|
|
min/max range values."""
|
|
self.ranges.append((range_type,range_min, range_max, argname))
|
|
|
|
def get_arg_names(self):
|
|
return [range_tuple[3] for range_tuple in self.ranges]
|
|
|
|
def get_target_opname(self):
|
|
"""Return the name of target operand of lookup function
|
|
(if it was supplied)"""
|
|
return self.target_op
|
|
|
|
def get_dimension_num(self):
|
|
"""Return the number of array dimensions.
|
|
In other words the number of the lookup function parameters."""
|
|
return len(self.ranges)
|
|
|
|
def get_values_space(self):
|
|
"""Return a list of all possible return values of a lookup function"""
|
|
return [val for (_idx_dict, val) in self.values]
|
|
|
|
def is_const_lookup_fun(self):
|
|
"""Return true if a lookup function always returns same value.
|
|
Const lookup function and the array is just a variable."""
|
|
return self.get_dimension_num() == 0
|
|
|
|
def add_value(self, indx_dict, value):
|
|
"""set the scalar value for the dictionary of indices"""
|
|
self.values.append((indx_dict, value))
|
|
|
|
def validate(self):
|
|
"""Make sure that all the index arrays have the expected
|
|
number of elements."""
|
|
expected_len = len(self.ranges)
|
|
for idict,value in self.values:
|
|
if len(list(idict.keys())) != expected_len:
|
|
return False
|
|
return True
|
|
|
|
def gen_lookup_function(self, fn_name, check_bounds=True, static=False,
|
|
inline=False, check_const=False):
|
|
"""Create a lookup function that will look up the value and
|
|
return the appropriate type. Typed args will be added for each
|
|
dimension
|
|
|
|
check_const argument is for checking if lookup function is of a form
|
|
return var; - constant function. Then we can optimize it to a form
|
|
return const; where const is a compile-time constant (a number).
|
|
"""
|
|
|
|
fo = function_object_t(fn_name, self.type, static=static, inline=inline)
|
|
|
|
#optimization for const functions
|
|
if check_const:
|
|
if self.is_const_lookup_fun() and len(self.values) == 1:
|
|
fo.add_code('/*Constant function*/')
|
|
value = self.values[0][1]
|
|
fo.add_code_eol('return %s' % str(value))
|
|
self.lookup_fn = fo
|
|
return
|
|
|
|
|
|
fo.add_code_eol(self.type + ' _v')
|
|
|
|
index_expression = ''
|
|
for i,range_tuple in enumerate(self.ranges):
|
|
range_type, range_min, range_max, argname = range_tuple
|
|
fo.add_arg(range_type + ' arg_' + argname)
|
|
index_expression += '[arg_%s]' % (argname)
|
|
lower_bound = str(range_min)
|
|
upper_bound = str(range_max)
|
|
if check_bounds:
|
|
# FIXME: if the range type is unsigned, and the lower
|
|
# bound is zero, then we need not check it. But it is
|
|
# hard to tell from here with an arbitrary type. ICL
|
|
# complains about this, warning/error #186.
|
|
fo.add_code_eol('xed_assert(arg_'+ argname + '>=' + lower_bound +
|
|
' && arg_' + argname + '<' + upper_bound + ')')
|
|
|
|
fo.add_code_eol('_v=' + self.name + index_expression)
|
|
|
|
fo.add_code_eol('return _v')
|
|
self.lookup_fn = fo
|
|
|
|
def emit_lookup_function(self):
|
|
"""Emit the lookup function as a string"""
|
|
if self.lookup_fn == None:
|
|
die("Need to generate the lookup function first for " + self.name)
|
|
return self.lookup_fn.emit()
|
|
|
|
def emit_lookup_function_header(self):
|
|
"""Emit the lookup function header as a string"""
|
|
if self.lookup_fn == None:
|
|
die("Need to generate the lookup function first for " + self.name)
|
|
return self.lookup_fn.emit_header()
|
|
|
|
def emit_initialization_function_header(self):
|
|
if self.init_fn == None:
|
|
die("Need to generate the init function first for " + self.name)
|
|
return self.init_fn.emit_header()
|
|
|
|
def emit_declaration(self, extern=False, static=False):
|
|
"""Emit the array declaration as a string."""
|
|
s = []
|
|
if static:
|
|
s.append( 'static ')
|
|
if extern:
|
|
s.append( 'extern ')
|
|
|
|
s.append( self.type + ' ' + self.name)
|
|
for range_type, range_min, range_max, argname in self.ranges:
|
|
s.append('[%s]' % (str(range_max)))
|
|
s.append(';\n')
|
|
return ''.join(s)
|
|
|
|
def compute_missing_values(self,key):
|
|
present_values = {}
|
|
for indices_dict,value in self.values:
|
|
if key in indices_dict:
|
|
present_values[indices_dict[key]] = True
|
|
return list(present_values.keys())
|
|
|
|
|
|
def make_initialization_function(self, init_function_name,verbose=False):
|
|
fo = function_object_t(init_function_name,'void')
|
|
lines = self.emit_initialization(verbose=verbose)
|
|
fo.add_lines(lines)
|
|
self.init_fn = fo
|
|
|
|
|
|
def emit_initialization(self,verbose=False): # private
|
|
"""Return a list of strings containing array initialization lines"""
|
|
lines = []
|
|
indices = [ x[3] for x in self.ranges] # get the argnames
|
|
|
|
missing_key = None
|
|
missed_one = True
|
|
# if we missed one then we process everything all over again to make sure we
|
|
# got everything. We only add one missing OD per pass
|
|
while missed_one:
|
|
missed_one = False
|
|
|
|
new_values = []
|
|
for indices_dict,value in self.values:
|
|
missing_key = None # 2007-06-16 bug fix. This line was missing
|
|
if verbose:
|
|
msge("AG: Processing value: %s indices: %s" % ( value, str(indices_dict)))
|
|
for key in indices:
|
|
if key not in indices_dict:
|
|
# we are missing values for this key.
|
|
if verbose:
|
|
msge("AG: Missing key %s" % (key))
|
|
missing_key = key
|
|
missed_one = True
|
|
if missing_key == None:
|
|
# We have this key, so add the dictionary
|
|
new_values.append((indices_dict,value))
|
|
else:
|
|
# We must replicate this entry for all values of the
|
|
# *all* missing keys! We can do this one missing field
|
|
# at a time. Eventually, we'll get them all.
|
|
all_present_values = self.compute_missing_values(missing_key)
|
|
if verbose:
|
|
msge('AG: Adding missing key %s to %s with values %s' % ( missing_key,
|
|
self.name,
|
|
str(all_present_values)))
|
|
for v in all_present_values:
|
|
new_dict = copy.copy(indices_dict)
|
|
new_dict[missing_key] = v
|
|
new_values.append((new_dict,value))
|
|
del self.values
|
|
self.values = new_values
|
|
|
|
|
|
for indices_dict,value in self.values:
|
|
s = [self.name]
|
|
for key in indices:
|
|
indx = indices_dict[key]
|
|
if type(indx) == dict:
|
|
die("A dictionary escaped during array init building for " + self.name)
|
|
s.append('[%s]' %(str(indx)))
|
|
s.append( '=%s;' % str(value) )
|
|
t = ''.join(s)
|
|
if verbose:
|
|
msge("AG: %s" % t)
|
|
lines.append(t)
|
|
return lines
|
|
|