1 # coding: utf-8
2 # frozen_string_literal: true
3
4 module Umu
5
6 module Commander
7
8 module Subcommand
9
10 module_function
11
12 def execute(line, line_num, env, setup_env)
13 ASSERT.kind_of line, ::String
14 ASSERT.kind_of line_num, ::Integer
15 ASSERT.kind_of env, E::Entry
16 ASSERT.kind_of setup_env, E::Entry
17
18 name, *args = line.split
19
20 new_env = case name
21 when ':trace'
22 env.update_trace_mode true
23 when ':notrace'
24 env.update_trace_mode false
25 when ':lextrace'
26 env.update_lex_trace_mode true
27 when ':nolextrace'
28 env.update_lex_trace_mode false
29 when ':dump'
30 env.update_dump_mode true
31 when ':nodump'
32 env.update_dump_mode false
33 when ':class'
34 case args.size
35 when 0
36 Subcommand.print_class_tree env.ty_context.root_class_signat
37 when 1
38 class_signat = env.ty_lookup(
39 args[0].to_sym,
40 LOC.make_location(STDIN_FILE_NAME, line_num)
41 )
42
43 Subcommand.print_class_signat class_signat, env
44 else
45 raise X::CommandError.new "Syntax error"
46 end
47
48 env
49 when ':env'
50 setup_bindings = setup_env.va_get_bindings
51
52 Subcommand.get_binding_lines(env){ |sym, value|
53 opt_setup_value = setup_bindings[sym]
54 if opt_setup_value
55 if opt_setup_value == value
56 false
57 else
58 true
59 end
60 else
61 true
62 end
63 }.each do |_sym, line|
64 STDERR.puts line
65 end
66
67 env
68 when ':envall'
69 Subcommand.get_binding_lines(env).sort { |a, b|
70 a[0] <=> b[0]
71 }.each do |_sym, line|
72 STDERR.puts line
73 end
74
75 env
76 else
77 raise X::CommandError.new "Unknown command: '%s'", line
78 end
79
80 ASSERT.kind_of new_env, E::Entry
81 end
82
83
84 def print_class_tree(class_signat, nest = 0)
85 ASSERT.kind_of class_signat, ECTSC::Abstract
86 ASSERT.kind_of nest, ::Integer
87
88 printf("%s%s%s\n",
89 ' ' * nest,
90 class_signat.to_sym.to_s,
91 class_signat.abstract_class? ? '/' : ''
92 )
93
94 class_signat.subclasses.sort.each do |subclass_signat|
95 Subcommand.print_class_tree subclass_signat, nest + 1
96 end
97 end
98
99
100 def print_class_signat(class_signat, env, nest = 0)
101 ASSERT.kind_of class_signat, ECTSC::Abstract
102 ASSERT.kind_of env, E::Entry
103 ASSERT.kind_of nest, ::Integer
104
105 indent_0 = ' ' * nest
106 indent_1 = ' ' * (nest + 1)
107
108 printf("%sABSTRACT CLASS?: %s\n",
109 indent_0,
110
111 if class_signat.abstract_class?
112 "Yes"
113 else
114 "No, this is a concrete class"
115 end
116 )
117
118 opt_superclass = class_signat.opt_superclass
119 if opt_superclass
120 superclass = opt_superclass
121
122 printf "%sSUPERCLASS: %s\n", indent_0, superclass.to_sym
123 end
124
125 subclasses = class_signat.subclasses
126 unless subclasses.empty?
127 printf("%sSUBCLASSES: %s\n",
128 indent_0,
129 subclasses.map(&:to_sym).join(', ')
130 )
131 end
132
133 ancestors = class_signat.ancestors
134 unless ancestors.empty?
135 printf("%sANCESTORS: %s\n",
136 indent_0,
137 ancestors.map(&:to_sym).join(', ')
138 )
139 end
140
141 descendants = class_signat.descendants
142 unless descendants.empty?
143 printf("%sDESCENDANTS: %s\n",
144 indent_0,
145 descendants.map(&:to_sym).join(', ')
146 )
147 end
148
149 class_message_infos_of_class_sym,
150 instance_message_infos_of_class_sym =
151 Subcommand.get_message_infos_of_class class_signat.klass, env
152
153 sub_cmess_infos_of_sym, *super_cmess_infos_of_sym =
154 class_message_infos_of_class_sym.to_a
155 class_sym_of_sub_cmess, sub_cmess_infos =
156 sub_cmess_infos_of_sym
157
158 sub_imess_infos_of_sym, *super_imess_infos_of_sym =
159 instance_message_infos_of_class_sym.to_a
160 class_sym_of_sub_imess, sub_imess_infos =
161 sub_imess_infos_of_sym
162
163 if super_cmess_infos_of_sym.any? { |_sym, infos|
164 ! infos.empty?
165 }
166 printf "%sINHERITED CLASS MESSAGES:\n", indent_0
167
168 super_cmess_infos_of_sym.reverse_each do |class_sym, infos|
169 unless infos.empty?
170 printf("%s INHERIT FROM: %s\n",
171 indent_0, class_sym.to_s
172 )
173
174 __print_class_message_list__(
175 indent_1,
176 class_sym.to_s,
177 infos
178 )
179 end
180 end
181 end
182
183 if sub_cmess_infos && ! sub_cmess_infos.empty?
184 printf "%sCLASS MESSAGES:\n", indent_0
185
186 __print_class_message_list__(
187 indent_1,
188 class_sym_of_sub_cmess.to_s,
189 sub_cmess_infos
190 )
191 end
192
193 if super_imess_infos_of_sym.any? { |_sym, infos|
194 ! infos.empty?
195 }
196 printf "%sINHERITED INSTANCE MESSAGES:\n", indent_0
197
198 super_imess_infos_of_sym.reverse_each do |class_sym, infos|
199 unless infos.empty?
200 printf("%s INHERIT FROM: %s\n",
201 indent_0, class_sym.to_s
202 )
203
204 __print_instance_message_list__(
205 indent_1,
206 class_sym.to_s,
207 infos
208 )
209 end
210 end
211 end
212
213 if sub_imess_infos && ! sub_imess_infos.empty?
214 printf "%sINSTANCE MESSAGES:\n", indent_0
215
216 __print_instance_message_list__(
217 indent_1,
218 class_sym_of_sub_imess.to_s,
219 sub_imess_infos
220 )
221 end
222 end
223
224
225 def __print_class_message_list__(indent, class_name, infos)
226 __format_message_infos__(infos).each do |info|
227 printf("%s&%s .%s\n",
228 indent,
229 class_name,
230 info
231 )
232 end
233 end
234
235
236 def __print_instance_message_list__(indent, class_name, infos)
237 __format_message_infos__(infos).each do |info|
238 printf("%s%s #%s\n",
239 indent,
240 class_name,
241 info
242 )
243 end
244 end
245
246
247 def __format_message_infos__(infos_1)
248 max_mess_length = infos_1.map { |info|
249 mess_name, opt_param_list, _ret_name = info.format_info
250
251 opt_param_list ? mess_name.length : 0
252 }.max
253
254 infos_2 = infos_1.map { |info|
255 mess_name, opt_param_list, ret_name = info.format_info
256
257 [
258 if opt_param_list
259 padding = " " * (max_mess_length - mess_name.length)
260
261 format("%s%s :", mess_name, padding)
262 else
263 mess_name
264 end
265 ] + [
266 opt_param_list,
267 ret_name
268 ]
269 }
270
271 max_body_length = infos_2.map {
272 |mess_name, opt_param_list, _ret_name|
273
274 (
275 mess_name + (
276 opt_param_list ? opt_param_list : ''
277 )
278 ).length
279 }.max
280
281 infos_2.map {
282 |mess_name, opt_param_list, ret_name|
283
284 if opt_param_list
285 padding = " " * (
286 max_body_length - (
287 mess_name + opt_param_list
288 ).length
289 )
290
291 if opt_param_list.length == 0
292 format("%s %s",
293 mess_name,
294 ret_name
295 )
296 else
297 format("%s %s%s -> %s",
298 mess_name,
299 opt_param_list,
300 padding,
301 ret_name
302 )
303 end
304 else
305 padding = " " * (max_body_length - mess_name.length)
306
307 format "%s%s -> %s", mess_name, padding, ret_name
308 end
309 }
310 end
311
312
313 def get_message_infos_of_class(klass, env)
314 ASSERT.subclass_of klass, VC::Top
315 ASSERT.kind_of env, E::Entry
316
317 pair = if klass <= VC::Class
318 [{}, {}]
319 else
320 loop.inject(
321 [
322 {},
323 {},
324 klass,
325 {},
326 {}
327 ]
328 ) {
329 |
330 (
331 cmess_infos_of_sym,
332 imess_infos_of_sym,
333 k,
334 set_of_cmess,
335 set_of_imess
336 ),
337
338 _
339 |
340
341 unless k <= VC::Top
342 break [cmess_infos_of_sym, imess_infos_of_sym]
343 end
344
345 cmess_infos = k.class_method_infos.reject { |info|
346 set_of_cmess[info.mess_sym] ||
347 /^%/ =~ info.mess_sym.to_s
348 }
349
350 imess_infos = k.instance_method_infos.reject { |info|
351 set_of_imess[info.mess_sym] ||
352 /^%/ =~ info.mess_sym.to_s
353 }
354
355 [
356 cmess_infos_of_sym.merge(k.type_sym => cmess_infos),
357
358 imess_infos_of_sym.merge(k.type_sym => imess_infos),
359
360 k.superclass,
361
362 cmess_infos.inject(set_of_cmess) { |set, info|
363 set.merge(info.mess_sym => true)
364 },
365
366 imess_infos.inject(set_of_imess) { |set, info|
367 set.merge(info.mess_sym => true)
368 }
369 ]
370 }
371 end
372
373 ASSERT.tuple_of pair, [::Hash, ::Hash]
374 end
375
376
377 def get_binding_lines(env, &_block)
378 ASSERT.kind_of env, E::Entry
379
380 lines = env.va_context.reverse_each.inject({}) { |lines, context|
381 ASSERT.kind_of lines, ::Hash
382 ASSERT.kind_of context, ECV::Entry
383
384 lines.merge(
385 context.get_bindings.select { |sym, value|
386 if /^%/ =~ sym.to_s
387 false
388 else
389 if block_given?
390 yield sym, value
391 else
392 true
393 end
394 end
395 }.inject({}) { |hash, (sym, value)|
396 line = case value
397 when VC::Fun
398 format "fun %s", sym.to_s
399 when VC::Struct::Entry
400 format "structure %s", sym.to_s
401 else
402 format("val %s : %s",
403 sym.to_s,
404 value.type_sym.to_s
405 )
406 end
407
408 hash.merge(sym => line) {
409 ASSERT.abort "No case: %s", sym
410 }
411 }
412 ) { |_sym, newer_line, _older_line|
413 newer_line
414 }
415 }
416
417 ASSERT.kind_of lines, ::Hash
418 end
419
420 end # Umu::Commander::Subcommand
421
422 end # Umu::Commander
423
424 end # Umu