假定有CPP 源码文件(.cpp) 仅有如下内容
有如上特点的CPP源码文件,为了实现与Python脚本兼容对接,
请基于Python 2.7.X 封装实现一个可重用的类,要求如下:
该类的类名要求为PyMacroParser,所提供的方法约定如下:
Input
假设源文件 a.cpp 中有如下内容
#ifndef MCTEST
#define MCTEST
#ifdef MC1
#define data1 0x20
/*cmment start*/#define /*this is comment*/ data2 2.5f
#define data3 L"this is a data"
#define data4 true
#ifdef MC2
#define data5 'a'
#define data6 { {2.0, "abc"}, {1.5, "def"}, {5.6f, "7.2"}} // 浮点与字符串组成的结构体初始化聚合, 再进一步聚合组成了数组
#else
#define data5 {5.0, 7.5, 3.8}
#define data6 'c'
#endif //end MC2
#else
#define data1 1.0f /* this is float
may be changed
*/
#define data2 2
#define data3 false
#define data4 "this is a data"
#ifdef MC2
#define data5 'B'
#define data6 {1, 6, 3}
#define data7 0xa
#else
#define data5 'D'
#define data6 {1, 6}
#endif //end MC2
#endif //MC1
#ifdef MC2
#undef MC2
#endif
#endif // !MC_TEST
python 测试代码:
a1 = PyMacroParser()
a2 = PyMacroParser()
a1.load("a.cpp")
filename = "b.cpp"
a1.dump(filename) #没有预定义宏的情况下,dump cpp
a2.load(filename)
a2.dumpDict()
a1.preDefine("MC1;MC2") #指定预定义宏,再dump
a1.dumpDict()
a1.dump("c.cpp")
Output
则b.cpp输出
#define data1 1.0 //浮点精度信息消失,统一转成了double 正式输出没有这个注释
#define data2 2
#define data3 false
#define data4 "this is a data"
#define data5 68 //注意:这里本是'D' 转换后成为整型十进制表示,正式输出没有这个注释
#define data6 {1, 6}
#define MCTEST //空宏,但是被定义了, 正式输出没有这个注释
a2.dump字典
{
"data1" : 1.0,
"data2" : 2,
"data3" : False,
"data4" : "this is a data",
"data5" : 68,
"data6" : (1, 6),
"MCTEST" : None, #空宏,但被定义了。 正式输出没有这个注释
}
a1.dump字典:
{
"data1" : 32,
"data2" : 2.5, #2.5f的float标记消失,正式输出没有这个注释
"data3" : u"this is a data", #宽字符串成为 unicode 正式输出没有这个注释
"data4" : True,
"data5" : 97, #注意 这里是'a'转int。 正式输出没有这个注释
"data6" : ((2.0, "abc"), (1.5, "def"), (5.6, "7.2")) , #python数据对象与源数据类型按规则对应即可, 正式输出没有这个注释
"MC1" : None, #预定义的空宏,而MC2最终被undef了,所以不存在MC2
"MCTEST" : None,
}
c.cpp 输出
#define data1 32 //16进制表示消失。 正式输出没有这个注释
#define data2 2.5
#define data3 L"this is a data" //unicode 转回宽字符 正式输出没有这个注释
#define data4 true
#define data5 97 //'a', 正式输出没有这个注释
#define data6 {{2.0, "abc"}, {1.5, "def"}, {5.6, "7.2"}} #tuple转回聚合, 正式输出没有这个注释
#define MC1
#define MCTEST
功能说明:load(self, f) 从指定文件中读取CPP宏定义,存为python内部数据,以备进一步解析使用。 f为文件路径,文件操作失败抛出异常;无返回值。若在初步解析中遇到宏定义格式错误 或 常量类型数据定义错误应该抛出异常。
需求分析:读取cpp宏定义,存为python内部数据
解题思路:按行读取cpp文件,去除不必要的换行符、Tab、转义、注释,留下有用的宏定义。将宏定义用函数进行解析。解析函数应当对宏定义进行分割和处理。并保存为python的List数据。
宏的定义一般是2个词,或3个词,中间空格分隔,常见的思路是split,这里有个小坑,如果定义的是字符串,字符串中间有空格,这样split会把第三个词割裂了。
关键函数:去除换行符、去除Tab、去除注释、处理转义字符、解析宏定义并保存。
功能说明: preDefine(self, s) 输入一堆预定义宏名串,宏名与宏名之间以;
分割。 preDefine函数可被反复调用,每次调用自动清理掉之前的预定义宏序列。 preDefine 与 load的CPP宏定义数据,一起决定最终可用的宏。
需求分析:读取输入的以;
分割的宏名串,存为python内部数据
解题思路:读取以;
分割的宏名串,处理转义字符,保存为python的List数据。
关键函数:转义字符处理
功能说明:dumpDict(self) 返回一个dict, 结合类中存储的CPP宏定义与预定义的宏序列,解析输出所有的可用宏到一个字典,其中宏名转为字符串后作为字典的key, 若有与宏名对应的常量转为python数据对象,作为字典的value,无常量则存为None, 注意不要返回类中内置的对象的引用。 解析过程若遇到宏定义格式错误 或 常量类型数据定义错误应该抛出异常;
需求分析:得到可用宏,输出到字典中
解题思路:将存储的cpp宏定义和预定义的宏都作为可用宏。解析所有的可用宏,用字典来存储,其中key为宏名转化成的字符串,value为对应的常量或None。
关键函数:解析宏(处理#ifdef/#ifndef/#else/#endif/#define/#undef 这几个宏指令)
功能说明:dump(self, f) 结合类中的CPP宏定义数据与预定义宏序列,解析输出所有可用宏存储到新的CPP源文件,f为CPP文件路径,文件若存在则覆盖,文件操作失败抛出异常。 若遇到宏定义格式错误 或 常量类型数据定义错误应该抛出异常。 注意,转换后的常量数据表示应与Python对应类型兼容, 所以常量类型的长度存储信息可能丢失(例如 short 转为 int; float 转为 double 等), 允许特别表示方法信息丢失(例如原本16进制 统一变成10进制表示等)。 导出宏的顺序不做特别要求。
需求分析:将输出的可用宏存储到cpp源文件
解题思路:首先将存储的cpp宏定义和预定义的宏都作为可用宏。解析所有的可用宏,用字典来存储,其中key为宏名转化成的字符串,value为对应的常量或None。之后,将字典数据存成cpp源文件,需要进行类型转换。
关键函数:解析宏、字典里的宏定义存为C类型的文件
class PyMacroParser:
def __init__(self):
self.myMacro=[] # 保存的宏定义
self.preDefineMacro=[] #预定义的宏
#读取cpp宏定义,存为python内部数据
def load(self,f):
try:
with open(f,'r')as f:
initLines = f.readlines()
#去除注释
macroLines = self.delComment(initLines)
#解析宏定义,存为python内部数据
self.myMacro = self.parseMacro(macroLines)
except IOError:
print("ERROR: %s 文件打开失败或文件不存在!" %f)
else:
print("%s 文件打开成功!" %f)
#预定义宏序列
def preDefine(self,s):
#处理转义字符
self.proEscapeChar(s)
# 清空之前的预定义宏序列
self.preDefineMacro = []
temp = s.strip().split(';')
for item in temp:
if item != "":
self.preDefineMacro.append(item.strip())
#输出所有的可用宏到字典里
def dumpDict(self):
macroDict = {}
for macroName in self.preDefineMacro:
macroDict[macroName] = None
self.parseDict(self.myMacro, macroDict)
res = {}
for key in macroDict:
res[key] = macroDict[key]
print(res)
return res
#输出所有的可用宏到cpp文件里
def dump(self,f):
macroDict = {}
for macroName in self.preDefineMacro:
macroDict[macroName] = None
self.parseDict(self.myMacro, macroDict)
try:
with open(f, 'w') as f:
for key in macroDict:
if macroDict[key] is not None:
f.write("#define {0} {1}\n".format(key, self.dictToCpp(macroDict[key])))
else:
f.write("#define {}\n".format(key))
except IOError:
print("Error: %s 文件写入失败!" %f)
else:
print("%s 文件写入成功!" %f)
我在test case 02_1和test case 02_6中都出现了问题。
第一次出错的原因是删除注释时出现了问题,第二次出错是对于宏命令格式解析出错。
出错原因无非以下几种:
1.删除注释时出错(如遇到TAB键、空格之类,以及对转义字符的处理)
2.宏定义解析时出错(特别注意# define
也是有效定义,以及遇到型如{{1},{2,},}
这种聚合类型定义时)
3.类型转换时出错(对字符串类型、字符类型、浮点类型、整型的分别处理)
欢迎交流