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
|
############################################################################
#
# File: utrim.icn
#
# Subject: Program to remove unneeded procs from ucode
#
# Author: Gregg M. Townsend
#
# Date: August 7, 1993
#
############################################################################
#
# This file is in the public domain.
#
############################################################################
#
# Usage: utrim [-s | -v] file...
#
# Utrim alters a set of uncode files comprising a complete Icon program
# by removing unreferenced procedures. The resulting files are smaller,
# and they produce a smaller icode file.
#
# The basename of each command argument is used to find a pair of
# .u1 and .u2 files; each pair is renamed to .u1o and .u2o and
# replaced by new .u1 and .u2 files.
#
# -s invokes silent mode; -v invokes verbose mode.
#
# Warning: utrim may break programs that use string invocation.
#
############################################################################
#
# Links: options
#
############################################################################
link options
record prc(name, size, calls, need) # proc record
record lcl(name, flags) # local record
global pnames, ptable # proc names and table
# main procedure
procedure main(args)
local opts, fname, name, need
# process options
opts := options(args, "sv")
if *args = 0 then
stop("usage: ", &progname, " [-s | -v] file.u1 ...")
every !args ?:= tab(upto('.'))
# scan .u1 files to decide what's needed
pnames := set()
ptable := table()
every scan1(!args)
if /ptable["main"] then
stop(&progname, ": no main procedure")
dependencies()
report(opts)
# write new .u1 and .u2 files
every fname := !args || (".u1" | ".u2") do {
remove(fname || "o")
rename(fname, fname || "o") | stop("can't rename ", fname)
}
every filter1(!args)
every filter2(!args)
end
# scan1(fname) -- read .u1 file, add proc names and refs to ptable
procedure scan1(fname)
local u1, line, i, name, flags, curr, locals
u1 := open(fname || ".u1") | stop(&progname, ": can't open", fname || ".u1")
while line := read(u1) do line ? {
if ="proc " then {
# new proc: make table entry
name := tab(0)
insert(pnames, name)
ptable[name] := curr := prc(name, 0, set())
locals := []
}
else if ="\tlocal\t" then {
# new local: remember its name
i := tab(many(&digits))
=","
flags := tab(upto(','))
=","
name := tab(0)
put(locals, lcl(name, flags))
}
else if ="\tvar\t" then {
# ref to "local": note as needed if it's a global
i := tab(0) + 1
if locals[i].flags = 0 then
insert(curr.calls, locals[i].name)
}
curr.size +:= 1 # tally number of lines
}
close(u1)
return
end
# dependencies() -- mark procs called directly or indirectly from main proc
procedure dependencies()
local need, p
need := ["main"]
while name := get(need) do
if (p := \ptable[name]) & (/p.need := 1) then
every put(need, !p.calls)
return
end
# report(opts) -- write reports as selected by command options
procedure report(opts)
local name, p, ptrim, ltrim, ltotal
ltotal := ltrim := ptrim := 0
every name := !sort(pnames) do {
p := ptable[name]
ltotal +:= p.size
if /p.need then {
ltrim +:= p.size
ptrim +:= 1
}
if /opts["v"] then
next
writes(right(p.size, 6))
writes(if \p.need then " * " else " ")
writes(left(p.name, 16))
every writes(" ", !sort(p.calls))
write()
}
if /opts["s"] then
write(&errout, "Trimming ", ptrim, "/", *pnames, " procedures (",
(100 * ptrim + 5) / *pnames, "%), ", ltrim, "/", ltotal, " lines (",
(100 * ltrim + 5) / ltotal, "%)")
return
end
# filter1(fname) -- filter .u1o file to make new .u1 file
#
# For each proc body, copy only if marked as needed in ptable.
procedure filter1(fname)
local old, new, line
old := open(fname||".u1o") | stop(&progname, ": can't open", fname||".u1o")
new := open(fname||".u1","w") | stop(&progname,": can't write",fname||".u1")
while line := read(old) do line ?
if ="proc " & /ptable[tab(0)].need then # check new proc
until (line ? ="\tend") | not (line := read(old)) # skip to proc end
else
write(new, line)
close(old)
close(new)
return
end
# filter2(fname) -- filter .u2o file to make new .u2 file
#
# Copy header verbatim; read list of globals, remove procs trimmed from .u1,
# and write new (renumbered) global list.
procedure filter2(fname)
local old, new, line, n, glist, flags, name, args, p
old := open(fname||".u2o") | stop(&progname, ": can't open ", fname||".u2o")
new := open(fname||".u2","w") | stop(&progname,": can't write ",fname||".u2")
write(new, read(old)) | stop(&progname, ": empty ", fname || ".u2o")
while (line := read(old)) & not (line ? ="global") do
write(new, line)
glist := []
while line := read(old) do line ? {
="\t"
tab(many(&digits))
p := &pos
=","
flags := tab(upto(','))
=","
name := tab(upto(','))
if flags = 5 & /(\ptable[name]).need then
next
tab(p)
put(glist, tab(0))
}
write(new, "global\t", *glist)
every write(new, "\t", 0 to *glist - 1, get(glist))
close(old)
close(new)
return
end
|