-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate.py
More file actions
138 lines (131 loc) · 4.9 KB
/
generate.py
File metadata and controls
138 lines (131 loc) · 4.9 KB
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
import style, lang_c, lang_cpp
import argparse, fnmatch, os, re, sys
parser=argparse.ArgumentParser(description=(
'This is a simple code generation framework. '+
'The main goal is to be able to generate target code inline with Python code. '+
'Inline Python code is marked with surrounding /*\\ and \\*/ markings, each on their own line. '+
'Inline Python code can call the functions store(name, value) and load(name) to store variables across blocks. '+
'Store a dictionary in __header__ or __footer__ that maps a file name regex to a metablock to append to each generated file. '+
'Inline Python code can access the path variable to get the folder containing the metasource. '+
'Inline Python code can access the relative_path variable to identify the metasource -- important for headers and footers. '+
'Inline Python code can set result to a string of target code, result is initialized as "". '+
'Each result of a metasource will be placed in the output folder with a parallel file structure, and .meta removed from its name.'
))
parser.add_argument('input', help='where to base paths off of')
parser.add_argument('--metasource', action='append', help='path to metasource to process and create an output from -- if unspecified, all *.meta* files in input are processed')
parser.add_argument('--script', default=[], action='append', help='path to script to process')
parser.add_argument('--output', default='.', help='path to put output')
parser.add_argument('--define', action='append', help='define a variable with a value, separate with an equal sign, like variable=value')
parser.add_argument('--suppress-origins', action='store_true', help='suppress comments in generated code showing which metasource line it came from')
args=parser.parse_args()
values={}
def store(name, value): values[name]=value
def load(name): return values[name]
def load_all(symbol_table):
for name, value in values.items(): symbol_table[name]=value
def exec_local(x, metasource):
p=os.path.split(os.path.realpath(metasource))[0]
sys.path.append(p)
scope={
'store': store,
'load': load,
'load_all': load_all,
'path': p,
'relative_path': metasource,
'style': style,
'lang_c': lang_c,
'lang_cpp': lang_cpp,
're': re,
'os': os,
'result': ''
}
exec(x, scope)
return scope['result']
if args.define:
for d in args.define:
name, value=d.split('=')
store(name, value)
for s in args.script:
full_path=os.path.join(args.input, s)
with open(full_path) as f: script=f.read()
try:
exec_local(script, full_path)
except Exception as e:
print('exception while running script '+full_path)
raise
def split_path(path):
result=[]
while path:
path, x=os.path.split(path)
result=[x]+result
return result
if not args.metasource:
args.metasource=[]
for root, dirs, files in os.walk(args.input):
args.metasource+=[os.path.join(*(split_path(root)[1:]+[i])) for i in fnmatch.filter(files, '*.meta*')]
for m in args.metasource:
full_path=os.path.join(args.input, m)
with open(full_path) as f: lines=f.readlines()
output=''
meta=False
metablock=''
tabs=0
for i in range(len(lines)):
if '/*\\' in lines[i]:
meta=True
tabs=len(re.match(r'(\t*)/\*\\', lines[i]).group(1))
elif '\\*/' in lines[i]:
meta=False
try:
result=exec_local(metablock, full_path)
except Exception as e:
import traceback
numbered_metablock=metablock.split('\n')
number_size=len(str(len(numbered_metablock)))
for j in range(len(numbered_metablock)):
format='{0:'+str(number_size)+'} '
numbered_metablock[j]=format.format(j+1)+numbered_metablock[j]
numbered_metablock='\n'.join(numbered_metablock)
print('exception raised executing metablock ending on line {} of {}'.format(i+1, m))
print(traceback.format_exc())
print(numbered_metablock)
sys.exit(1)
result=result.strip().split('\n')
for j in range(len(result)):
result[j]='\t'*tabs+result[j]
if not args.suppress_origins:
for j in range(len(result)):
comment={
'c': '//',
'h': '//',
'cpp': '//',
'hpp': '//',
'java': '//',
'py': '#',
}
extension=m.split('.')[-1]
if extension in comment:
result[j]=result[j]+'{}{}:{}'.format(comment[extension], m, i+1)
result='\n'.join(result)+'\n'
output+=result
metablock=''
elif meta:
if re.search(r'\S', lines[i]):
if lines[i][:tabs]!='\t'*tabs:
raise Exception('indentation error on line {}'.format(i+1))
metablock+=lines[i][tabs:]
else: output+=lines[i]
path=os.path.join(args.output, m.replace('.meta', ''))
try: os.makedirs(os.path.split(path)[0])
except: pass
if '__header__' in values:
for pattern, header in values['__header__'].items():
if re.match(pattern, m):
output=exec_local(header, full_path)+output
break
if '__footer__' in values:
for pattern, footer in values['__footer__'].items():
if re.match(pattern, m):
output=output+exec_local(footer, full_path)
break
with open(path, 'w') as f: f.write(output)