-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerator.py
224 lines (205 loc) · 9.33 KB
/
generator.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
#!/usr/bin/python
# This is a hairbrained script that extracts gcc's error messages in order to generate a pretty table describing
# how template and auto type deduction works in C++11.
#
# This script assumes you have gcc version 4.8.3 installed. I've also tested and confirmed that it works with
# gcc __ on cygwin. Since this script relies on regexp to extract content from gcc's error messages, it's pretty
# brittle and will likely break with future versions of gcc. If you've got said future version, then then have some
# fun and update the regexp! :-).
#
#
# More specifically, this script does the following:
#
# - We have a list of substitutions below in a list called 'substitutions'.
# - We open the file 'templateFile.cpp', then
# - for each substitution:
# - we replace the word SUBSTITUTION_POINT (in templateFile.cpp) with the substitution, then
# - we compile the substituted code with gcc, then
# - gcc will fail when it it tries to instantiate the IAmA template (invoked via the whatTheHeckAreYou macro).
# - If the 'whatTheHeckAreYou' macro is invoked within a template then we can extract the type T for the
# template.
# - Regardless of where 'whatTheHeckAreYou' is invoked, we can extract the type of the expression passed to
# this macro.
#
# For example, if (in templateFile.cpp) we substitute SUBSTITUTION_POINT with 'lvalRef(var)', where var is an integer
# variable and lval is the following function template:
#
# template<typename T>
# void lvalRef(T& x) { whatTheHeckAreYou(x); }
#
# then when we compile it with 'gcc -std=c++11 templatefile.cpp', gcc will print the following (cryptic) error messages:
#
# ./templateFile.cpp: In instantiation of 'void lvalRef(T&) [with T = int]':
# ./templateFile.cpp:27:15: required from here
# ./templateFile.cpp:9:26: error: 'IAmA<int&> blah' has incomplete type
# IAmA<decltype(expr)> blah;
# ^
# ./templateFile.cpp:12:19: note: in expansion of macro 'whatTheHeckAreYou'
# void lval(T& x) { whatTheHeckAreYou(x); }
#
# Notice this reveals that the type of T in our call to lval is int, and the type of x (as is passed to the IAmA
# template) is int&.
#
# The nice thing this script does for you is repeatedly call gcc with different substitutions, parse the error messages,
# and then present the results in a table.
import subprocess;
import re;
# The following two regexps are used to extract the type of T and type of expr from the error message.
# See the comments above for an example of the type of error message we see from GCC.
# As GCC evolves we may have to update these regexps
REGEXP_TYPE_OF_T = r"\[with T = (.*)\]"
REGEXP_TYPE_OF_EXPR = r"IAmA<([^>]*)>"
# Substitutions to make. Have fun and insert your own, comment out the existing ones:
# Have even more fun, change the code in 'templateFile.cpp'.
substitutions = [""
"whatTheHeckAreYou( var )"
,"whatTheHeckAreYou( constVar )"
,"whatTheHeckAreYou( reference )"
,"whatTheHeckAreYou( constReference )"
,"whatTheHeckAreYou( 42 )"
,""
,"lvalRef( var )"
,"lvalRef( constVar )"
,"lvalRef( reference )"
,"lvalRef( constReference )"
,"lvalRef( 42 )"
,""
,"lval( var )"
,"lval( constVar )"
,"lval( reference )"
,"lval( constReference )"
,"lval( 42 )"
,""
#,"lvalConst( var )"
#,"lvalConst( constVar )"
#,"lvalConst( reference )"
#,"lvalConst( constReference )"
#,"lvalConst( 42 )"
#,""
#,"lvalConstRef( var )"
#,"lvalConstRef( constVar )"
#,"lvalConstRef( reference )"
#,"lvalConstRef( constReference )"
#,"lvalConstRef( 42 )"
#,""
#,"rvalRef( var )"
#,"rvalRef( constVar )"
#,"rvalRef( reference )"
#,"rvalRef( constReference )"
#,"rvalRef( 42 )"
#,""
#,"whatTheHeckAreYou( auto_var )"
#,"whatTheHeckAreYou( auto_constVar )"
#,"whatTheHeckAreYou( auto_reference )"
#,"whatTheHeckAreYou( auto_constReference )"
#,""
#,"whatTheHeckAreYou( auto_ref_var )"
#,"whatTheHeckAreYou( auto_ref_constVar )"
#,"whatTheHeckAreYou( auto_ref_reference )"
#,"whatTheHeckAreYou( auto_ref_constReference )"
#,""
#,"whatTheHeckAreYou( auto_cref_var )"
#,"whatTheHeckAreYou( auto_cref_constVar )"
#,"whatTheHeckAreYou( auto_cref_reference )"
#,"whatTheHeckAreYou( auto_cref_constReference )"
#, ""
#,"whatTheHeckAreYou( auto_rref_var )"
#,"whatTheHeckAreYou( auto_rref_constVar )"
#,"whatTheHeckAreYou( auto_rref_reference )"
#,"whatTheHeckAreYou( auto_rref_constReference )"
#,"whatTheHeckAreYou( auto_rref_42 )"
#,"whatTheHeckAreYou( auto_rref_rvalue_int )"
#,"whatTheHeckAreYou( auto_rref_rvalue_cint )"
#,"whatTheHeckAreYou( auto_rref_value_rint )"
#,"whatTheHeckAreYou( auto_rref_value_crint )"
#,""
#,"whatTheHeckAreYou( array )"
#,"justVal( array )"
#,"lval( array )"
#,""
#,"whatTheHeckAreYou( initList )"
#,"lval( {1,2,3,4,5} )"
#,"lvalConst( {1,2,3,4,5} )"
#,"lvalRef( {1,2,3,4,5} )"
#,"lvalConstRef( {1,2,3,4,5} )"
#,"rvalRef( {1,2,3,4,5} )"
#,""
#,"whatTheHeckAreYou( fcn )"
#,"whatTheHeckAreYou( array )"
# ... <<<add your own substitutions here>>> ...
];
#-------------------------------
# Open templateFile.cpp and print out its contents to the user #
#-------------------------------
inputFile = open("templateFile.cpp").readlines();
print("recall, our Code is:")
print()
print(' ', ' '.join(inputFile))
#-------------------------------
# Print table header
#-------------------------------
subColWidth = len(max(substitutions, key=len)) + 2
typeColWidth = 30;
colHeader0 = "SUBSTITUTION"
colHeader1 = "type of T"
colHeader2 = "type of expr"
col0SpaceAfterLabel = subColWidth - len(colHeader0)
col1SpaceAfterLabel = typeColWidth - len(colHeader1)
col2SpaceAfterLabel = typeColWidth - len(colHeader2)
tableFormat = " | %%-%ds | %%-%ds | %%-%ds |" % (subColWidth, typeColWidth, typeColWidth)
print(" .%s%s," % ("-" * (subColWidth), "-" * (typeColWidth*2+8)))
print(" | %s%s | %s%s | %s%s |" % (
colHeader0, " " * col0SpaceAfterLabel,
colHeader1, " " * col1SpaceAfterLabel,
colHeader2, " " * col2SpaceAfterLabel))
print(" |%s|" % ("-" * (subColWidth + typeColWidth*2 + 8)))
#-------------------------------
# Generate rows of table (one row for each substitution to make)
#-------------------------------
hasErrorCase = False
for sub in substitutions:
# Do a break in the table if sub is an empty string
if sub == "":
print(tableFormat % ("", "", ""))
continue
# Open output file, and substitute 'SUBSTITUTION_POINT' with sub
outputFile = open("generateFile.cpp", 'w');
for line in inputFile:
line = line.replace("SUBSTITUTION_POINT", sub + ";");
outputFile.write(line)
outputFile.close()
# Run gcc and capture its error messages
gccProcess = subprocess.Popen(["gcc", "-std=c++11", "generateFile.cpp"], stderr=subprocess.PIPE);
output = str(gccProcess.communicate()[1])
# Using regexp, get the type for T and the expression passed to the 'whatTheHeckAreYou' macro
tType = None
paramType = None
for line in output.splitlines():
if tType == None:
m = re.search(REGEXP_TYPE_OF_T, line)
if m:
tType = m.group(1)
if paramType == None:
m = re.search(REGEXP_TYPE_OF_EXPR, line)
if m:
paramType = m.group(1)
# If we didn't match it's probably because we got the following error message
# like the following:
#
# cannot bind non-const lvalue reference of type int& to an rvalue of type int
#
# Really we should be more robust and search for that specific error message. But
# for now we just make this assumption.
if paramType is None:
paramType = "* <Error>"
hasErrorCase = True
# Print the row
print(tableFormat % (sub, tType, paramType))
#-------------------------------
# Print table footer
#-------------------------------
print(" `%s%s'" % ("-" * (subColWidth), "-" * (typeColWidth*2+8)))
print("Has Error?", hasErrorCase)
if hasErrorCase:
print("* This case did not match the expected pattern. Likely because you attempted to")
print(" bind a reference to a constant value.")