1 # $Id: node.rb,v 1.5 2012/04/17 02:19:08 machan Exp $
2
3 require 'tmdoc/tmstd'
4 require 'tmdoc/constant'
5 require 'tmdoc/environment'
6 require 'tmdoc/model/core'
7 require 'tmdoc/reader/ruby1.8/scanner'
8 require 'tmdoc/reader/ruby1.8/parser/types'
9 require 'tmdoc/reader/ruby1.8/parser/common'
10 require 'tmdoc/reader/ruby1.8/parser/parser'
11
12
13 module TmDoc
14
15 module Reader::Ruby18
16
17 module Parser
18
19 module Node
20
21 module Module
22 include RubyToken
23
24 module_function
25
26 def parse(scanner, above_path, context, env, m_tk)
27 ASSERT.kind_of scanner, Scanner
28 ASSERT.kind_of above_path, MCLP::AbsolutePath
29 ASSERT.kind_of context, Context
30 ASSERT.kind_of env, ENV::Environment
31 ASSERT.kind_of m_tk, TkMODULE
32
33 LOG::Debug.msgout "CALLED " +
34 "above_path='#{above_path.to_s}', " +
35 "m_tk=#{m_tk.inspect}" if env.debug_parser?
36
37 scanner.skip_space_or_nl!
38
39 name_tk = scanner.peek
40 mc_module =
41 case name_tk
42 when TkCONSTANT, TkCOLON2, TkCOLON3
43 name = Common.get_constant(scanner, env)
44 LOG::Debug.log(
45 "module #{name} [##{__LINE__} in #{__FILE__}]"
46 )if env.debug_parser?
47
48 name_path =
49 if /^::(.+)/ =~ name
50 MCLP::AbsolutePath.new($1.split(/::/))
51 else
52 MCLP::RelativePath.new(name.split(/::/))
53 end
54 mc_subjects = Statements.parse(
55 scanner,
56 if name_path.kind_of?(MCLP::AbsolutePath)
57 name_path
58 else
59 above_path + name_path
60 end,
61 context.update_module_func(false),
62 env
63 )
64 ASSERT.kind_of mc_subjects, MCLA::SetOfSubject
65
66 LOG::Debug.msgout("ACCEPT %s",
67 mc_module.to_s
68 ) if env.debug_parser?
69
70 MCLN::Module.new(
71 m_tk.line_no, mc_subjects, name_path
72 )
73
74 else
75 LOG::Warning.log(
76 "I can't recognize this token: %s<%s>" +
77 "for module name.\n" +
78 "\t-- #%d in '%s'",
79 if name_tk.respond_to?(:name)
80 name_tk.name
81 else
82 ''
83 end,
84 name_tk.class.to_s,
85 name_tk.line_no, scanner.file_name
86 )
87 CMD.test_sensitive_level env, LOG::Warning
88
89 nil
90 end
91
92 ASSERT.opt_kind_of mc_module, MCLN::Module
93 end
94 end
95
96
97
98 #################################
99 #### Parse class declaration ####
100 #################################
101
102 module Class
103 include RubyToken
104
105 module_function
106
107 def parse(scanner, above_path, context, env, c_tk)
108 ASSERT.kind_of scanner, Scanner
109 ASSERT.kind_of above_path, MCLP::AbsolutePath
110 ASSERT.kind_of context, Context
111 ASSERT.kind_of env, ENV::Environment
112 ASSERT.kind_of c_tk, TkCLASS
113
114 LOG::Debug.msgout "CALLED " +
115 "above_path='#{above_path.to_s}', " +
116 "c_tk=#{c_tk.inspect}" if env.debug_parser?
117
118 scanner.skip_space_or_nl!
119
120 peek_tk = scanner.peek
121
122 mc_class =
123 case peek_tk
124 when TkCONSTANT, TkCOLON2, TkCOLON3
125 name_tk = nil
126 name = Common.get_constant(scanner, env)
127 LOG::Debug.log(
128 "class #{name} [##{__LINE__} in #{__FILE__}]"
129 )if env.debug_parser?
130
131 scanner.skip_space!
132
133 is_valid_superclass, superclass_name =
134 parse_superclass_name(scanner, above_path, env)
135
136 if is_valid_superclass
137 name_path =
138 if /^::(.+)/ =~ name
139 MCLP::AbsolutePath.new($1.split(/::/))
140 else
141 MCLP::RelativePath.new(name.split(/::/))
142 end
143 mc_subjects = Statements.parse(
144 scanner,
145 if name_path.kind_of?(MCLP::AbsolutePath)
146 name_path
147 else
148 above_path + name_path
149 end,
150 context.update_module_func(false),
151 env
152 )
153 ASSERT.kind_of mc_subjects, MCLA::SetOfSubject
154
155 LOG::Debug.msgout("ACCEPT %s",
156 mc_class.to_s
157 ) if env.debug_parser?
158
159 MCLN::Class.new(
160 c_tk.line_no, mc_subjects, name_path,
161 superclass_name
162 )
163 else
164 nil
165 end
166
167 when TkLSHFT # '<<'
168 scanner.get!
169
170 scanner.skip_space_or_nl!
171
172 attached_name =
173 parse_uniqueclass_name(scanner, above_path, env)
174
175 if attached_name
176 mc_subjects = Statements.parse(
177 scanner,
178 above_path,
179 context.update_unique_class(true),
180 env
181 )
182 ASSERT.kind_of mc_subjects, MCLA::SetOfSubject
183
184 MCLN::Block.new(c_tk.line_no, mc_subjects)
185 else
186
187 nil
188 end
189
190 else
191 LOG::Warning.log(
192 "I can't recognize this token: %s<%s> " +
193 "for class name.\n" +
194 "\t-- #%d in '%s'",
195 if peek_tk.respond_to?(:name)
196 peek_tk.name
197 else
198 ''
199 end,
200 peek_tk.class.to_s,
201 peek_tk.line_no, scanner.file_name
202 )
203 CMD.test_sensitive_level env, LOG::Warning
204
205 nil
206 end
207
208 # MCLN::Class, MCLN::Block, or nil
209 ASSERT.opt_kind_of mc_class, MCLA::Subject
210 end
211
212
213 def parse_superclass_name(scanner, above_path, env)
214 ASSERT.kind_of scanner, Scanner
215 ASSERT.kind_of above_path, MCLP::AbsolutePath
216 ASSERT.kind_of env, ENV::Environment
217
218 is_valid_superclass, superclass_name =
219 if scanner.peek.is_a?(TkLT)
220 scanner.get!
221
222 scanner.skip_space_or_nl!
223
224 peek_tk = scanner.peek
225 case peek_tk
226 when TkCONSTANT, TkCOLON2, TkCOLON3
227 sc_name = Common.get_constant(scanner, env)
228 ASSERT.assert(! sc_name.empty?)
229
230 LOG::Debug.log "class '#{name}' < " +
231 "'#{sc_name}' " +
232 "[##{__LINE__} in " +
233 "#{__FILE__}]" if env.debug_parser?
234
235 [true, sc_name]
236
237 when TkSELF
238
239 [true, above_path.to_s]
240
241 else
242 LOG::Warning.log(
243 "The class definiation expected constant " +
244 "for superclass name, but: %s<%s>.\n" +
245 "\t-- #%d in '%s'",
246 if peek_tk.respond_to?(:name)
247 peek_tk.name
248 else
249 ''
250 end,
251 peek_tk.class.to_s,
252 peek_tk.line_no, scanner.file_name
253 )
254 CMD.test_sensitive_level env, LOG::Warning
255
256 [false, nil]
257 end
258 else
259 [true, nil]
260 end
261
262 ASSERT.boolean is_valid_superclass
263 ASSERT.opt_kind_of superclass_name, String
264 [is_valid_superclass, superclass_name]
265 end
266
267
268 def parse_uniqueclass_name(scanner, above_path, env)
269 ASSERT.kind_of scanner, Scanner
270 ASSERT.kind_of above_path, MCLP::AbsolutePath
271 ASSERT.kind_of env, ENV::Environment
272
273 peek_tk = scanner.peek
274
275 attached_name =
276 case peek_tk
277 when TkCONSTANT
278 ac_name = Common.get_constant(scanner, env)
279 ASSERT.assert(! ac_name.empty?)
280
281 LOG::Debug.log "unique class << '#{ac_name}' " +
282 "[##{__LINE__} in " +
283 "#{__FILE__}]" if env.debug_parser?
284
285 if above_path.empty? || above_path.last != ac_name
286 LOG::Warning.log(
287 "The unique class definiation expected " +
288 "above class or module " +
289 "for attach class, but: <%s>.\n" +
290 "\t-- #%d in '%s'",
291 ac_name,
292 peek_tk.line_no, scanner.file_name
293 )
294 CMD.test_sensitive_level env, LOG::Warning
295
296 nil
297 else
298 ac_name
299 end
300 when TkSELF
301
302 above_path.to_s
303
304 when TkCOLON2, TkCOLON3
305 LOG::Warning.log(
306 "I can't recognize the attach class " +
307 "with absolute path.\n" +
308 "\t-- #%d in '%s'",
309 peek_tk.line_no, scanner.file_name
310 )
311 CMD.test_sensitive_level env, LOG::Warning
312
313 nil
314
315 else
316 LOG::Warning.log(
317 "The unique class definiation expected " +
318 "constant or 'self' " +
319 "for attach class name, but: %s<%s>.\n" +
320 "\t-- #%d in '%s'",
321 if peek_tk.respond_to?(:name)
322 peek_tk.name
323 else
324 ''
325 end,
326 peek_tk.class.to_s,
327 peek_tk.line_no, scanner.file_name
328 )
329 CMD.test_sensitive_level env, LOG::Warning
330
331 nil
332 end
333
334 ASSERT.opt_kind_of attached_name, String
335 end
336 end
337
338
339 ##################################
340 #### Parse method declaration ####
341 ##################################
342
343 module Method
344 include RubyToken
345
346 module_function
347
348 def parse(scanner, above_path, context, env, m_tk)
349 ASSERT.kind_of scanner, Scanner
350 ASSERT.kind_of above_path, MCLP::AbsolutePath
351 ASSERT.kind_of context, Context
352 ASSERT.kind_of env, ENV::Environment
353 ASSERT.kind_of m_tk, TkDEF
354
355 LOG::Debug.msgout "CALLED " +
356 "above_path='#{above_path.to_s}', " +
357 "m_tk=#{m_tk.inspect}" if env.debug_parser?
358
359 scanner.skip_space_or_nl!
360 name_tk = scanner.get!
361 back_tk = scanner.skip_space_or_nl!
362 dot_tk = scanner.get!
363
364 result, is_qualified, name =
365 if dot_tk.kind_of?(TkDOT) || dot_tk.kind_of?(TkCOLON2)
366 scanner.lex_state! EXPR_FNAME
367 scanner.skip_space_or_nl!
368 name_tk2 = scanner.get!
369
370 result_name = parse_qualified_method_name(
371 name_tk, name_tk2, scanner, above_path, env
372 )
373
374 if result_name
375 [true, true, result_name]
376 else
377 [false, nil, nil]
378 end
379 else
380 scanner.unget! dot_tk
381 back_tk.reverse_each do |tk|
382 scanner.unget! tk
383 end
384
385 result_name = parse_unqualified_method_name(
386 name_tk, scanner, env
387 )
388
389 if result_name
390 [true, false, result_name]
391 else
392 [false, nil, nil]
393 end
394 end
395 ASSERT.boolean result
396 return nil unless result
397 ASSERT.boolean is_qualified
398 ASSERT.kind_of name, String
399
400 params = parse_method_parameters scanner, env
401 LOG::Debug.log "params=#{params.inspect} " +
402 "[##{__LINE__}]" if env.debug_parser?
403 str_args =
404 if params
405 /\(([^\)]+)\)/ =~ params
406 if $1
407 $1.split(',').map { |str_arg| str_arg.strip }
408 else
409 []
410 end
411 else
412 []
413 end
414
415 mc_method =
416 if name
417 if context.in_unique_class? && is_qualified
418 LOG::Warning.log(
419 "I can't recognize the unique method: <%s>" +
420 " in unique class definiation.\n" +
421 "\t-- #%d in '%s'",
422 name,
423 tk.line_no, scanner.file_name
424 )
425 CMD.test_sensitive_level env, LOG::Warning
426
427 nil
428 else
429 mc_subjects = Statements.parse(
430 scanner,
431 above_path,
432 context.update_module_func(false),
433 env
434 )
435 ASSERT.kind_of mc_subjects, MCLA::SetOfSubject
436
437 ASSERT.boolean context.in_module_func?
438
439 LOG::Debug.msgout("ACCEPT %s",
440 mc_method.to_s
441 ) if env.debug_parser?
442
443 MCLN::Method.new(
444 m_tk.line_no,
445 name,
446 mc_subjects,
447 LSM::SeqOfString.new(str_args),
448 nil,
449 if is_qualified || context.in_module_func?
450 false
451 else
452 ! context.in_unique_class?
453 end,
454 context.in_module_func?
455 )
456 end
457 else
458 LOG::Warning.log(
459 "I can't recognize as method name.\n" +
460 "\t-- #%d in '%s'",
461 name_tk.line_no,
462 scanner.file_nme
463 )
464 CMD.test_sensitive_level env, LOG::Warning
465
466 nil
467 end
468
469 ASSERT.opt_kind_of mc_method, MCLN::Method
470 end
471
472
473 def parse_qualified_method_name(
474 qualify_tk, name_tk, scanner, above_path, env
475 )
476 ASSERT.kind_of qualify_tk, RubyToken::Token
477 ASSERT.kind_of name_tk, RubyToken::Token
478 ASSERT.kind_of scanner, Scanner
479 ASSERT.kind_of above_path, MCLP::AbsolutePath
480 ASSERT.kind_of env, ENV::Environment
481
482 result_name =
483 if name_tk.respond_to?(:name)
484 case qualify_tk
485 when TkSELF # 'self'
486 ASSERT.kind_of name_tk.name, String
487
488 name_tk.name
489
490 when TkId
491 if above_path.length >= 1 &&
492 above_path.last == qualify_tk.name
493 ASSERT.kind_of name_tk.name, String
494
495 name_tk.name
496 else
497 LOG::Warning.log(
498 "I can't recognize the TkId token: <%s> " +
499 " for module name of the unique method.\n" +
500 "\t-- #%d in '%s'",
501 qualify_tk.name,
502 qualify_tk.line_no, scanner.file_name
503 )
504 CMD.test_sensitive_level env, LOG::Warning
505
506 nil
507 end
508
509 else
510 LOG::Warning.log(
511 "I can't recognize the token: <%s> " +
512 "for module name of the unique method: %s<%s>.\n" +
513 "\t-- #%d in '%s'",
514 if qualify_tk.respond_to?(:name)
515 qualify_tk.name
516 else
517 ''
518 end,
519 qualify_tk.class.to_s,
520 qualify_tk.name,
521 qualify_tk.line_no, scanner.file_name
522 )
523 CMD.test_sensitive_level env, LOG::Warning
524
525 nil
526 end
527 else
528 LOG::Warning.log(
529 "I can't recognize the token: %s<%s>" +
530 " for unique method.\n" +
531 "\t-- #%d in '%s'",
532 if qualify_tk.respond_to?(:name)
533 qualify_tk.name
534 else
535 ''
536 end,
537 name_tk.class.to_s, name_tk.line_no, scanner.file_name
538 )
539 CMD.test_sensitive_level env, LOG::Warning
540
541 nil
542 end
543
544 ASSERT.opt_kind_of result_name, String
545 end
546
547
548 def parse_unqualified_method_name(name_tk, scanner, env)
549 ASSERT.kind_of name_tk, RubyToken::Token
550 ASSERT.kind_of scanner, Scanner
551 ASSERT.kind_of env, ENV::Environment
552
553 result_name =
554 if name_tk.respond_to?(:name)
555 ASSERT.kind_of name_tk.name, String
556
557 name_tk.name
558 elsif name_tk.respond_to?(:value)
559 ASSERT.kind_of name_tk.value, String
560
561 name_tk.value
562 else
563 LOG::Warning.log(
564 "I can't recognize the token: %s<%s>" +
565 " for instance method.\n" +
566 "\t-- #%d in '%s'",
567 if name_tk.respond_to?(:name)
568 name_tk.name
569 else
570 ''
571 end,
572 name_tk.class.to_s,
573 name_tk.line_no, scanner.file_name
574 )
575 CMD.test_sensitive_level env, LOG::Warning
576
577 nil
578 end
579
580 ASSERT.opt_kind_of result_name, String
581 end
582
583
584 def parse_method_parameters(scanner, env)
585 ASSERT.kind_of scanner, Scanner
586 ASSERT.kind_of env, ENV::Environment
587
588 LOG::Debug.msgout "CALLED" if env.debug_parser?
589
590 scanner.skip_space!
591 scanner.get_readed!
592
593 tk = scanner.get!
594 end_token =
595 case tk
596 when TkLPAREN, TkfLPAREN
597 TkRPAREN
598 when TkRPAREN
599 return ""
600 else
601 TkNL
602 end
603
604 nest = 0
605 loop do
606 LOG::Debug.log "Param: " +
607 "#{tk.inspect} #{nest}" if env.debug_parser?
608
609 case tk
610 when TkSEMICOLON
611 break
612 when TkLBRACE
613 nest += 1
614 when TkRBRACE
615 scanner.unget!(tk) if nest.zero?
616 nest -= 1
617 break if nest <= 0
618 when TkLPAREN, TkfLPAREN
619 nest += 1
620 when end_token
621 if end_token == TkRPAREN
622 nest -= 1
623
624 if nest <= 0
625 break
626 end
627 else
628 break
629 end
630 else
631 ; # nop
632 end
633
634 tk = scanner.get!
635 end
636 res = scanner.get_readed!.tr("\n", " ").strip
637 if res == ";"
638 res = nil
639 end
640
641 LOG::Debug.msgout "RETURN #{res}" if env.debug_parser?
642
643 ASSERT.opt_kind_of res, String
644 end
645 end
646
647 end # TmDoc::Reader::Ruby18::Parser::Node
648
649 end # TmDoc::Reader::Ruby18::Parser
650
651 end # TmDoc::Reader::Ruby18
652
653 end # TmDoc