-
Notifications
You must be signed in to change notification settings - Fork 0
/
c++pack.py
133 lines (115 loc) · 3.81 KB
/
c++pack.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
#!/bin/python3
import argparse
import os
import string
from enum import Enum
def doInline(filepath, seen=set()):
if filepath in seen:
return "", seen
seen.add(filepath)
outcode = f"// BEGIN PACKED FROM {filepath}\n"
for line in open(filepath, "r").readlines():
if line.startswith("#include \""):
libraryname = line.partition("\"")[2].partition("\"")[0]
filebase, _ = os.path.split(filepath)
librarypath = os.path.join(filebase, libraryname)
currcode, seen = doInline(librarypath, seen)
if "\ufeff" in currcode:
print(currcode)
raise Exception
outcode += currcode
elif not line.startswith("#pragma once"):
outcode += line
if "\ufeff" in line:
print(line)
raise Exception
outcode += "\n"
outcode += f"// END PACKED FROM {filepath}\n"
outcode += "\n"
return outcode, seen
def lexer(code):
code = code.replace("\t", "")
for _ in range(30):
code = code.replace(" ", " ")
match = {"//": "\n", "/*": "*/", "\"": "\"", "'": "'", "#": "\n"}
mode = "none"
lexemes = [""]
while code:
if mode == "none":
starts = [k for k in match if code.startswith(k)]
if starts:
assert len(starts) == 1
mode = starts[0]
lexemes.append(mode)
code = code[len(mode):]
else:
lexemes[-1] += code[0]
code = code[1:]
else:
if code.startswith(match[mode]):
lexemes[-1] += match[mode]
lexemes.append("")
code = code[len(match[mode]):]
mode = "none"
else:
lexemes[-1] += code[0]
code = code[1:]
lexemes = [x.replace("\n", "") for x in lexemes]
lexemes = [x for x in lexemes if x not in ["", " "]]
return lexemes
def remComments(code):
lexemes = [x for x in lexer(code) if not x.startswith(
"//") and not x.startswith("/*")]
out = ["\n"+x+"\n" if x.startswith("#") else x for x in lexemes]
return "".join(out).replace("\n\n", "\n").strip("\n")
def remWhitespace(code):
varchars = string.ascii_letters + string.digits + "_" + "'\""
out = ""
for lexeme in lexer(code):
line = ""
for prev, curr, after in zip(lexeme, lexeme[1:], lexeme[2:]):
if curr == " " and (prev not in varchars or after not in varchars):
pass
else:
line += curr
if lexeme[0] == "#":
out += "\n" + lexeme[0] + line + lexeme[-1] + "\n"
else:
out += lexeme[0] + line + lexeme[-1]
return out.replace("\n\n", "\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="inline all custom library imports"
)
parser.add_argument(
"filename",
metavar="filename",
type=str,
help="the file path of the c++ code to be packed"
)
parser.add_argument(
"-o", dest="output_suffix",
type=str,
default="_",
help="destination file for pack"
)
parser.add_argument(
"--no_comments",
action="store_true"
)
parser.add_argument(
"--no_whitespace",
action="store_true"
)
args = parser.parse_args()
assert args.filename.endswith(".cpp")
filepath = os.path.join(os.getcwd(), args.filename)
code, libraries = doInline(filepath)
# print(libraries)
if args.no_comments:
code = remComments(code)
if args.no_whitespace:
code = remWhitespace(code)
assert args.output_suffix != ""
output_filename = args.filename[:-4] + args.output_suffix + ".cpp"
open(output_filename, "w").write(code)