1 // Read a set of lines on stdin of the following form:
6 // Delete all 'dead' definitions with following indented lines that aren't
7 // used outside their bodies.
9 // This can be transitive; deleting one definition may cause other definitions
12 // Also assorts segments as a side-effect.
14 // Like linkify, treeshake is a hack.
22 #define SIZE(X) (assert((X).size() < (1LL<<(sizeof(int)*8-2))), static_cast<int>((X).size()))
33 using std::istringstream
;
35 bool starts_with(const string
& s
, const string
& pat
) {
36 string::const_iterator a
=s
.begin(), b
=pat
.begin();
37 for (/*nada*/; a
!=s
.end() && b
!=pat
.end(); ++a
, ++b
)
38 if (*a
!= *b
) return false;
39 return b
== pat
.end();
44 void read_body(string name
, string definition_line
, map
<string
, vector
<string
> >& segment
) {
45 // last definition wins; this only matters for the 'Entry' label in the code segment
46 segment
[name
] = vector
<string
>();
47 segment
[name
].push_back(definition_line
);
49 if (cin
.peek() != ' ' && cin
.peek() != '$') break; // assumes: no whitespace but spaces; internal labels start with '$'
52 segment
[name
].push_back(line
);
56 void read_lines(string segment_header
, map
<string
, vector
<string
> >& segment
) {
57 // first segment header wins
59 segment
["=="].push_back(segment_header
); // '==' is a special key containing the segment header
61 if (cin
.peek() == '=') break; // assumes: no line can start with '=' except a segment header
62 assert(cin
.peek() != ' '); // assumes: no whitespace but spaces
65 istringstream
lstream(line
);
67 getline(lstream
, name
, ' ');
68 assert(name
[SIZE(name
)-1] == ':');
69 name
.erase(--name
.end());
70 read_body(name
, line
, segment
);
74 void read_lines(map
<string
, vector
<string
> >& code
, map
<string
, vector
<string
> >& data
) {
78 assert(starts_with(line
, "== "));
79 map
<string
, vector
<string
> >& curr
= (line
.substr(3, 4) == "code") ? code
: data
; // HACK: doesn't support segments except 'code' and 'data'
80 read_lines(line
, curr
);
86 bool any_line_matches(string pat
, const vector
<string
>& lines
) {
87 for (int i
= 0; i
< SIZE(lines
); ++i
)
88 if (lines
.at(i
).find(pat
) != string::npos
) // conservative: confused by word boundaries, comments and string literals
93 bool is_dead(string key
, const map
<string
, vector
<string
> >& code
, const map
<string
, vector
<string
> >& data
) {
94 if (key
== "Entry") return false;
95 if (key
== "==") return false;
96 for (map
<string
, vector
<string
> >::const_iterator p
= code
.begin(); p
!= code
.end(); ++p
) {
97 if (p
->first
== key
) continue;
98 if (any_line_matches(key
, p
->second
)) return false;
100 for (map
<string
, vector
<string
> >::const_iterator p
= data
.begin(); p
!= data
.end(); ++p
) {
101 if (p
->first
== key
) continue;
102 if (any_line_matches(key
, p
->second
)) return false;
107 void treeshake(map
<string
, vector
<string
> >& code
, map
<string
, vector
<string
> >& data
) {
108 for (map
<string
, vector
<string
> >::iterator p
= code
.begin(); p
!= code
.end(); ++p
) {
109 if (is_dead(p
->first
, code
, data
)) {
110 //? cerr << " erasing " << p->first << '\n';
115 for (map
<string
, vector
<string
> >::iterator p
= data
.begin(); p
!= data
.end(); ++p
) {
116 if (is_dead(p
->first
, code
, data
)) {
117 //? cerr << " erasing " << p->first << '\n';
126 void dump(const map
<string
, vector
<string
> > definitions
) {
127 // nothing special needed for segment headers, since '=' precedes all alphabet in ASCII
128 for (map
<string
, vector
<string
> >::const_iterator p
= definitions
.begin(); p
!= definitions
.end(); ++p
) {
129 const vector
<string
>& lines
= p
->second
;
130 for (int i
= 0; i
< SIZE(lines
); ++i
)
131 cout
<< lines
[i
] << '\n';
136 map
<string
, vector
<string
> > code
, data
;
137 read_lines(code
, data
);
138 for (int iter
= 0; ; ++iter
) {
139 //? cerr << "iter: " << iter << '\n';
140 int old_csize
= SIZE(code
), old_dsize
= SIZE(data
);
141 treeshake(code
, data
);
142 if (SIZE(code
) == old_csize
&& SIZE(data
) == old_dsize
) break;