-
Notifications
You must be signed in to change notification settings - Fork 17
/
compile.py
324 lines (279 loc) · 11.6 KB
/
compile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
import os
import subprocess
import json
import re
import shutil
import template_parser as tpp
import script_runner as sr
import argparse
from template_parser import TemplateParser
from termcolor import colored
class Compile:
sign_pattern = re.compile(r'#([a-zA-Z0-9_ ]+)$')
@staticmethod
def parse_dependencies(cmd: str, dependencies: dict, root='.', iters_on=None, ands_on=None):
if not iters_on:
iters_on, ands_on = Compile.split_loop(cmd)
replacment = {key: [] for key in iters_on}
patterns = {key: [re.compile(os.path.join(root, rep)) for rep in dependencies[key]]
for key in iters_on}
for root, dirs, files in os.walk(root):
for file in files:
path = os.path.join(root, file)
for key, pt_list in patterns.items():
for pt in pt_list:
if pt.search(path) is not None:
replacment[key].append(path)
return replacment
@staticmethod
def split_loop(cmd: str):
iter_pt = re.compile(r'\{\!([^}\n]*)\}')
fn_pt = re.compile(r'\{\&([^}\n]*)\}')
iters_on = iter_pt.findall(cmd)
iters_on = list(map(str.strip, iters_on))
ands_on = fn_pt.findall(cmd)
ands_on = list(map(str.strip, ands_on))
ands_on = list(filter(lambda x: x not in iters_on, ands_on))
return iters_on, ands_on
@staticmethod
def iteration_cmd_generator(cmd: str, iter_on, replacment):
try:
var = next(iter_on)
except StopIteration:
return [cmd, ]
cmd_tps = []
for value in replacment[var]:
tmp = cmd.replace('{!%s}' % var, value)
vp = os.path.split(value)[-1].split('.')
tmp = tmp.replace('{&%s}' % var, vp[-2] if len(vp) > 1 else vp[0])
cmd_tps.extend(Compile.iteration_cmd_generator(tmp, iter_on, replacment))
return cmd_tps
@staticmethod
def run_cmd(cmd: str, PIPE_IN=None, echo=True):
print(cmd)
if PIPE_IN:
p = subprocess.Popen(cmd.split(' '), stdin=subprocess.PIPE)
p.communicate(PIPE_IN)
else:
p = subprocess.Popen(cmd.split(' '))
rt_value = p.wait()
return rt_value
@staticmethod
def get_cmd_templates(cmd: str, iter_params, root='.', iters_on=None, ands_on=None):
signs = Compile.sign_pattern.findall(cmd)
mode = 'SINGLE'
if len(signs) > 1:
raise SyntaxError(cmd)
elif len(signs) == 1:
signs = list(map(str.lower, signs[0].split(' ')))
if 'batch' in signs:
mode = 'BATCH'
cmd = Compile.sign_pattern.sub('', cmd).strip()
if not iters_on:
iters_on, ands_on = Compile.split_loop(cmd)
if mode == 'SINGLE':
cmd_templates = Compile.iteration_cmd_generator(cmd, iter(iters_on), iter_params)
else:
for iter_on in iters_on:
cmd = cmd.replace('{!%s}' % iter_on, ' '.join(iter_params[iter_on]))
cmd_templates = [cmd, ]
return cmd_templates, signs
@staticmethod
def replace_normal_and(cmd_templates, dependencies, ands_on):
for and_on in ands_on:
value = dependencies[and_on]
vp = os.path.split(value)[-1].split('.')
cmd_templates = list(map(lambda x: x.replace('{&%s}' % and_on,
vp[-2] if len(vp) > 1 else vp[0]), cmd_templates))
return cmd_templates
@staticmethod
def process_cmd(cmd: str, dependencies, root='.'):
iters_on, ands_on = Compile.split_loop(cmd)
iter_params = Compile.parse_dependencies(cmd, dependencies, root, iters_on)
cmd_templates, signs = Compile.get_cmd_templates(cmd, iter_params, root)
cmd_templates = Compile.replace_normal_and(cmd_templates, dependencies, ands_on)
cmd_list = list(map(lambda x: x.format(**dependencies), cmd_templates))
return cmd_list, signs
class ConfigParser:
def __init__(self, json_path, root='.'):
self.config = json.load(open(json_path))
self.root = os.path.abspath(root)
self.general = self.config.get('general')
self.__load_config__()
def __load_config__(self):
pass
def combine(self, file_path: str, tp_path: str, func_name='logic_bomb'):
def params_list_parser(params):
if len(params.strip()) == 0:
return []
else:
params = params.split(',')
params = list(map(str.strip, params))
res = []
var_pattern = re.compile(r'([a-zA-Z_][a-zA-Z0-9_*]*|\*+)')
for param in params:
tmp = var_pattern.findall(param)
if len(tmp) < 2:
raise SyntaxError(', '.join(params))
var_name = tmp[-1]
var_type = ' '.join(tmp[:-1])
var_type = re.sub(r'[ \t\n]*\*', '*', var_type)
res.append((var_type, var_name))
return res
func_pattern = re.compile(r'int[ \t\n]+%s\(([^)]*)\);*' % func_name)
with open(file_path) as f:
pg = f.read()
finds = func_pattern.findall(pg)
if len(finds) == 0:
return None
elif len(finds) > 1:
raise SyntaxError(repr(finds) + '. Duplicated definition!')
else:
params = finds[0].strip()
params_list = params_list_parser(params)
# Generate definitions
defs = []
call_params = []
for v_type, var in params_list:
defs.append(' '.join([v_type, var]) + ';')
call_params.append(var)
params = dict(
vars=call_params,
params=', '.join(call_params),
defs='\n'.join(defs)
)
res = TemplateParser.appender_parser(tp_path, params)
res = '\n'.join([pg, res])
return res
def normal_compiler(self, prefix: str):
config = self.config.get(prefix, None)
if not config:
return None
cmds = config.get('cmd', None)
dependencies = config.get('dependencies', None)
if not cmds or not dependencies:
return None
mkdirs = config.get('mkdir', [])
for mkdir in mkdirs:
if not os.path.exists(mkdir):
os.makedirs(mkdir)
for cmd in cmds:
dependencies['CC'] = self.general['CC']
cmd_list, signs = Compile.process_cmd(cmd, dependencies, self.root)
for gonna_run in cmd_list:
Compile.run_cmd(gonna_run)
rms = config.get('rm', [])
for rm in rms:
shutil.rmtree(rm)
return True
def pipe_compile(self, prefix: str, func_name='logic_bomb', echo=False):
def params_list_parser(params):
if len(params.strip()) == 0:
return []
else:
params = params.split(',')
params = list(map(str.strip, params))
res = []
var_pattern = re.compile(r'([a-zA-Z_][a-zA-Z0-9_*]*|\*+)')
for param in params:
tmp = var_pattern.findall(param)
if len(tmp) < 2:
raise SyntaxError(', '.join(params))
var_name = tmp[-1]
var_type = ' '.join(tmp[:-1])
var_type = re.sub(r'[ \t\n]*\*', '*', var_type)
res.append((var_type, var_name))
return res
def combine(path, tp_path):
func_pattern = re.compile(r'int[ \t\n]+%s\(([^)]*)\);*' % func_name)
fp = path
print('-----------------------------')
print(fp)
with open(fp) as f:
content = f.read()
finds = func_pattern.findall(content)
if len(finds) > 1:
raise SyntaxError(repr(finds) + '. Duplicated definition!')
elif len(finds) == 0:
return None
else:
params = finds[0].strip()
params_list = params_list_parser(params)
vars_list = [_[1] for _ in params_list]
params = ', '.join(vars_list)
params_list_with_length = []
comments_pattern = re.compile(r'//(.*)\n[ \t]*' + r'int[ \t\n]+%s\(([^)]*)\);*' % func_name)
cmts = comments_pattern.findall(content)
cmt = cmts[0] if len(cmts) > 0 else ('{}', )
cmt_dict = json.loads(cmt[0])
for var_type, var_name in params_list:
length = cmt_dict.get(var_name, {}).get('length', 0)
params_list_with_length.append((var_type, var_name, length))
init_vars = dict(vp=params_list_with_length, params=params)
tp = tpp.TemplateParser(tp_path)
sruner = sr.ScriptRunner(init_vars)
res = sruner.run(tp.parse()[0])
res = '\n'.join(res[1])
res = tp.replace([res, ])
if echo:
print(res)
res = '\n'.join([content, res])
return res
config = self.config.get(prefix, None)
if not config:
return None
cmds = config.get('cmd', None)
dependencies = config.get('dependencies', None)
if not cmds or not dependencies:
return None
TP = dependencies.get('TEMPLATE', None)
FNAME = dependencies.get('FILENAME', 'FILENAME')
PATH = dependencies.get('PATH', None)
if not TP or not PATH:
print('here', 3)
return None
for mkdir in config.get('mkdir', []):
if not os.path.exists(mkdir):
os.makedirs(mkdir)
excepts = config.get('exceptions', [])
for root, dirs, files in os.walk(PATH):
for file in files:
path = os.path.join(root, file)
flag = False
for exct in excepts:
if path.startswith(exct):
flag = True
break
if flag:
continue
for cmd in cmds:
dependencies['CC'] = self.general['CC'] if not config.get('CC', False) else config.get('CC')
dependencies[FNAME] = path
cmd_list, signs = Compile.process_cmd(cmd, dependencies)
combined = combine(os.path.join(root, file), TP)
for gonna_run in cmd_list:
if combined is None:
continue
if 'pipe' in signs:
Compile.run_cmd(gonna_run, PIPE_IN=combined.encode('utf8'))
for rm in config.get('rm', []):
shutil.rmtree(rm)
return True
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--all_code", help="compile all", action="store_true")
parser.add_argument("-l", "--lib", help="compile lib", action="store_true")
parser.add_argument("-s", "--src", help="compile src", action="store_true")
parser.add_argument("-e", "--echo", help="echo main", action="store_true")
args = parser.parse_args()
all_code = args.all_code
lib = args.lib
src = args.src
echo = args.echo
c = ConfigParser('config/compile.json')
if lib or all_code:
c.normal_compiler('crypto_lib')
c.normal_compiler('utils_lib')
if src or all_code:
c.pipe_compile('src', echo=echo)
c.pipe_compile('src_cpp', echo=echo)