File: reader/ruby1.8/parser/node.rb

Overview
Module Structure
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: TmDoc#13
  module: Reader
  module: Ruby18#15
  module: Parser#17
  module: Node#19
  module: Module#21
includes
  RubyToken ( Builtin-Module )
has properties
function: parse / 5 #26
  module: Class#102
includes
  RubyToken ( Builtin-Module )
has properties
function: parse / 5 #107
function: parse_superclass_name / 3 #213
function: parse_uniqueclass_name / 3 #268
  module: Method#343
includes
  RubyToken ( Builtin-Module )
has properties
function: parse / 5 #348
function: parse_qualified_method_name / 5 #473
function: parse_unqualified_method_name / 3 #548
function: parse_method_parameters / 2 #584

Code

   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