ghidra/Ghidra/Extensions/SleighDevTools/pcodetest/tpp.py

213 lines
6.3 KiB
Python

#!/usr/bin/python
import re
import os
import sys
import glob
import argparse
class tpp:
def __init__(self, fname):
self.data = {'name':'', 'ifdef':'', 'main':'', 'body':'', 'num':''}
self.info = []
self.c_file = None
self.line_num = 0
self.fname = fname
def c_write(self, line):
if not self.c_file: self.c_write(line)
else: self.c_file.write(line + '\n')
def test_hdr(self, line):
self.c_write(line);
def test_test(self, name):
self.data['name'] = name
def test_if(self, line):
if self.data['name']: self.test_body(line)
elif self.data['ifdef']:
sys.stderr.write('ERROR: nested ifdef not allowed in file %s at line %d\n' % (self.fname, self.line_num))
sys.exit(1);
else: self.data['ifdef'] = line.strip()
def test_endif(self, line):
if self.data['name']: self.test_body(line)
def test_open_brace(self):
self.data['body'] = ''
def test_main(self, main):
self.data['main'] = main
self.c_write('''
extern void %(main)s(TestInfo*);
#define %(main)s_NUMB 0
static const char %(main)s_NAME [] = "%(main)s";
''' % self.data)
self.data['name'] = ''
self.data['ifdef'] = ''
self.data['body'] = ''
self.data['num'] = ''
def test_close_brace(self):
if not self.data['name']: return
self.c_write('')
if self.data['ifdef']: self.c_write(self.data['ifdef'])
self.data['num'] = str(len(re.findall(r'^\s+ASSERT', self.data['body'], flags=re.MULTILINE)))
self.c_write('''#define %(name)s_NUMB %(num)s
static const char %(name)s_NAME [] = "%(name)s";
static void %(name)s()
{
noteTestMain(__FILE__, __LINE__, %(name)s_NAME);
{
%(body)s\t}
breakOnSubDone(__FILE__, __LINE__, %(name)s_NAME);
}''' % self.data)
if self.data['ifdef']: self.c_write('#endif /* %(ifdef)s */\n' % self.data)
self.info += [(self.data['name'], self.data['ifdef'])]
# clear this test
self.data['name'] = ''
self.data['ifdef'] = ''
self.data['body'] = ''
def test_body(self, line):
if self.data['name']:
# add an indentation
if line[0] == '\t': line = '\t' + line
self.data['body'] += line
else:
self.c_write(line)
def test_fi(self):
self.c_write('static FunctionInfo fi[] = {')
if self.data['main']: self.c_write('\t{ %(main)s_NAME, (testFuncPtr) &%(main)s, %(main)s_NUMB },' % self.data)
for (e, f) in self.info:
if f: self.c_write(f)
self.c_write('\t{ %s_NAME, (testFuncPtr) &%s, %s_NUMB },' % (e, e, e))
if f: self.c_write('#endif /* %s */' % f)
self.c_write('\t{ 0, 0, 0 }')
self.c_write('};')
# This is boilerplate, supplying the main, etc
def test_boilerplate(self):
self.c_write('''
static GroupInfo Info = {
{\'a\', \'B\', \'c\', \'D\', \'e\', \'f\', \'G\', \'h\'},
fi
};
/* Function exists to make sure that the GroupInfo structure does not
* get optimized away.
**/
GroupInfo *%(main)s_Force() {
return &Info;
}
void %(main)s(TestInfo* not_used) {
i4 i = 0;
int numTest = 0;
TestInfo_reset();
for (i = 1; Info.funcTable[i].name; i++) Info.funcTable[i].func();
breakOnDone(__FILE__, __LINE__, %(main)s_NAME);
}''' % self.data)
def match(self, rexp, line):
self.m = re.match(rexp, line)
return self.m
# parse the test file
def parse(self):
if not self.fname.endswith('.test'):
sys.stderr.write('ERROR: filename %s must end with .test\n' % self.fname)
sys.exit(1);
self.c_file = open(re.sub('[.]test', '.c', self.fname), "w")
self.line_num = 0
for line in open(self.fname):
self.line_num += 1
if self.match(r'TEST\s+(\w*).*', line):
self.test_test(self.m.group(1))
elif self.match(r'(?:#include)\s+.*', line):
self.test_hdr(line)
elif self.match(r'(?:#if|#ifdef)\s+.*', line):
self.test_if(line)
elif self.match(r'#endif.*', line):
self.test_endif(line)
elif self.match(r'{\s*(.*)', line):
self.test_open_brace()
elif self.match(r'MAIN\s+(\w*).*', line):
self.test_main(self.m.group(1))
elif self.match(r'}.*', line):
self.test_close_brace()
else:
self.test_body(line)
self.test_fi()
self.test_boilerplate()
self.c_file.close()
self.c_file = False
# the ENTRY function will contain a call to all of the MAIN
# functions found in .test files in the current directory
def create_entry(self):
if os.path.exists(self.fname):
sys.stderr.write('WARNING: entry filename %s exists\n' % self.fname)
return;
extern_lines = []
main_lines = []
for tname in glob.glob(re.sub(r'[^/]*$', '*.test', self.fname)):
with open(tname) as tfile:
for line in tfile:
if self.match(r'MAIN\s+(\w*).*', line):
extern_lines.append('\textern void %s(TestInfo* not_used);' % self.m.group(1))
main_lines.append('\t%s(&info);' % self.m.group(1))
self.c_file = open(self.fname, "w")
self.c_write('#include "pcode_test.h"')
self.c_write('')
#for l in extern_lines:
# self.c_write(l)
self.c_write('void main(void) {')
self.c_write('\tTestInfo info;')
#for l in main_lines:
# self.c_write(l)
self.c_write('#ifdef BUILD_EXE')
self.c_write('\texit(0);')
self.c_write('#endif')
self.c_write('}')
self.c_file.close()
self.c_file = False
parser = argparse.ArgumentParser(description='Precompile test file',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('test_file', nargs='*', help='Test file to preprocess, must end with .test')
parser.add_argument('--entry', default='', help='Create file ENTRY contianing a main function that calls all MAIN functions')
sys.argv.pop(0)
args = parser.parse_args(sys.argv)
if args.test_file:
for test_file in args.test_file:
tpp(test_file).parse()
if args.entry:
tpp(args.entry).create_entry()