Class: BitClust::RRDParser::Context

Inherits:
Object
  • Object
show all
Includes:
NameUtils
Defined in:
lib/bitclust/rrdparser.rb

Defined Under Namespace

Classes: MethodAttributes

Constant Summary collapse

METHOD_ATTRIBUTES =

現在サポートするメソッド属性(... 属性行のトークン)。

  • nomethod/undef: 裸語。kind はエントリ単位でしか持てないので、 別名(複数シグネチャ)のエントリでは全シグネチャに同じ属性が 付いていることを要求する
  • since="X"/until="X"(bitclust#132 P4): シグネチャ単位で束縛され、 そのシグネチャの名前だけに適用される。nomethod/undef と違い、 別名ごとに異なる値を持てる(全シグネチャ一致は要求しない)のが 本来の用途。X は "3.2" のように数字とドットのみ。 空値(since="")は「明示的に不明」= バッジ非表示の指定で、メソッド 自体は昔からあるのにドキュメント追加が遅れ、バージョンラダーからの 自動算出が誤った版を出す場合の抑止に使う(空文字が記録されるため 算出値で上書きされず、表示側も空はバッジを出さない)
%w[nomethod undef]
KV_METHOD_ATTRIBUTES =
%w[since until]
KV_METHOD_ATTRIBUTE_VALUE_RE =
/\A(?:\d+(?:\.\d+)*)?\z/
MD_METHOD_SIG_PREFIX_RE =

md の ### def name .../### module_function def name .../ ### const name/### gvar $name シグネチャ行を rd 形式 (--- name ...)へ正規化するための接頭辞(MDParser::SIG_RE と 同じパターン)。属性の紐付け先となるメソッド名を MethodSignature.parse で取り出すために使う

/\A### (?:module_function def |def |const |gvar )/

Constants included from NameUtils

NameUtils::CHAR_TO_MARK, NameUtils::CHAR_TO_NAME, NameUtils::CLASS_NAME_RE, NameUtils::CLASS_PATH_RE, NameUtils::CONST_PATH_RE, NameUtils::CONST_RE, NameUtils::GVAR_RE, NameUtils::LIBNAME_RE, NameUtils::MARK_TO_CHAR, NameUtils::MARK_TO_NAME, NameUtils::METHOD_NAME_RE, NameUtils::METHOD_SPEC_RE, NameUtils::MID, NameUtils::NAME_TO_CHAR, NameUtils::NAME_TO_MARK, NameUtils::TYPEMARK_RE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from NameUtils

build_method_id, classid2name, classname2id, classname?, decodeid, decodename_fs, decodename_url, display_typemark, encodeid, encodename_fs, encodename_rdocurl, encodename_url, functionname?, gvarname?, html_filename, libid2name, libname2id, libname?, method_spec?, methodid2classid, methodid2libid, methodid2mname, methodid2specparts, methodid2specstring, methodid2typechar, methodid2typemark, methodid2typename, methodname?, split_method_id, split_method_spec, typechar2mark, typechar2name, typechar?, typemark2char, typemark2name, typemark?, typename2char, typename2mark, typename?

Constructor Details

#initialize(db, libname) ⇒ Context

Returns a new instance of Context.



353
354
355
356
357
358
359
360
# File 'lib/bitclust/rrdparser.rb', line 353

def initialize(db, libname)
  @db = db
  #@library = @db.open_library(libname)
  @library = @db.open_library(libname, true)   # FIXME: always reopen
  @klass = nil
  @type = nil
  @visibility = nil
end

Instance Attribute Details

#klassObject (readonly)

Returns the value of attribute klass.



363
364
365
# File 'lib/bitclust/rrdparser.rb', line 363

def klass
  @klass
end

#libraryObject (readonly)

Returns the value of attribute library.



362
363
364
# File 'lib/bitclust/rrdparser.rb', line 362

def library
  @library
end

#typeObject

Returns the value of attribute type.



364
365
366
# File 'lib/bitclust/rrdparser.rb', line 364

def type
  @type
end

#visibilityObject

Returns the value of attribute visibility.



365
366
367
# File 'lib/bitclust/rrdparser.rb', line 365

def visibility
  @visibility
end

Instance Method Details

#alias(name) ⇒ Object

Add a alias name to the alias list.



449
450
451
452
453
454
455
456
457
458
459
# File 'lib/bitclust/rrdparser.rb', line 449

def alias(name)
  klass = @klass || raise
  @db.open_class(name) do |c|
    c.type = klass.type
    c.library = @library
    c.aliasof = klass
    c.source = "Alias of [[c:#{klass.name}]]\n"
    @library.add_class c
    klass.alias c
  end
end

#categorize(category) ⇒ Object



367
368
369
# File 'lib/bitclust/rrdparser.rb', line 367

def categorize(category)
  @library.category = category
end

#constantObject



465
466
467
# File 'lib/bitclust/rrdparser.rb', line 465

def constant
  @type = :constant
end

#define_class(name, supername, location: nil) ⇒ Object



379
380
381
382
383
384
385
386
387
# File 'lib/bitclust/rrdparser.rb', line 379

def define_class(name, supername, location: nil)
  if @db.properties['version'] >= "1.9.0"
    top = 'BasicObject'
  else
    top = 'Object'
  end
  superclass = (name == top ? nil : @db.get_class(supername))
  register_class :class, name, superclass, location: location
end

#define_method(chunk) ⇒ Object



481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/bitclust/rrdparser.rb', line 481

def define_method(chunk)
  id = method_id(chunk)
  attrs = method_attributes(chunk)
  @db.open_method(id) {|m|
    m.names           = chunk.names.sort
    m.kind            = if attrs.flags.include?('undef')
                          :undefined
                        elsif attrs.flags.include?('nomethod')
                          :nomethod
                        else
                          @kind
                        end
    # steep:ignore:start
    m.visibility      = @visibility || :public
    # steep:ignore:end
    m.source          = chunk.source
    m.source_location = chunk.source.location
    attrs.since_until.each do |name, kv|
      m.fill_since(name, kv['since'] || raise) if kv['since']
      m.fill_until(name, kv['until'] || raise) if kv['until']
    end
    case @kind
    when :added, :redefined
      @library.add_method m
    end
  }
end

#define_module(name, location: nil) ⇒ Object



389
390
391
# File 'lib/bitclust/rrdparser.rb', line 389

def define_module(name, location: nil)
  register_class :module, name, nil, location: location
end

#define_object(name, singleton_object_class, location: nil) ⇒ Object



393
394
395
396
397
398
# File 'lib/bitclust/rrdparser.rb', line 393

def define_object(name, singleton_object_class, location: nil)
  singleton_object_class = @db.get_class(singleton_object_class) if singleton_object_class
  # steep:ignore:start
  register_class :object, name, singleton_object_class, location: location
  # steep:ignore:end
end

#dynamic_extend(name) ⇒ Object



444
445
446
# File 'lib/bitclust/rrdparser.rb', line 444

def dynamic_extend(name)
  @klass&.dynamic_extend(@db.get_class(name), @library)
end

#dynamic_include(name) ⇒ Object



440
441
442
# File 'lib/bitclust/rrdparser.rb', line 440

def dynamic_include(name)
  @klass&.dynamic_include(@db.get_class(name), @library)
end

#extend(name) ⇒ Object



436
437
438
# File 'lib/bitclust/rrdparser.rb', line 436

def extend(name)
  @klass&.extend @db.get_class(name)
end

#include(name) ⇒ Object



432
433
434
# File 'lib/bitclust/rrdparser.rb', line 432

def include(name)
  @klass&.include @db.get_class(name)
end

#method_attributes(chunk) ⇒ Object

kramdown Block IAL 風の ... 属性行からメタデータを集める。 属性行は「直前のシグネチャ行のみ」に束縛される(kramdown と同じ解釈)。 裸語トークン(nomethod/undef)は kind がエントリ単位でしか持てないため 全シグネチャで同じであることを要求するが、since=/until= はシグネチャ 単位でそのままの名前に適用するため、この一致要求からは除外する。 本文に入ったら探索を打ち切る(コード例中の ... を誤検出しないため)



545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
# File 'lib/bitclust/rrdparser.rb', line 545

def method_attributes(chunk)
  per_sig = [] #: Array[Array[String]]
  since_until = {} #: Hash[String, Hash[String, String]]
  current_name = nil #: String?
  current_keys = nil #: Hash[String, bool]?
  chunk.source.each_line do |line_|
    line = line_.chomp
    case line
    when /\A---\s/, /\A\#\#\#\s/
      per_sig.push []
      current_name = method_attribute_target_name(line)
      current_keys = {}
    when /\A\{:(.*)\}[ \t]*\z/
      break if per_sig.empty?
      ($1 || raise).strip.split(/\s+/).each do |token|
        if METHOD_ATTRIBUTES.include?(token)
          per_sig.last&.push token
        else
          key, value = parse_kv_method_attribute(token, chunk)
          keys = current_keys or raise
          if keys.key?(key)
            raise ParseError,
                  "#{chunk.source.location}: duplicate method attribute #{key.inspect} on the same signature"
          end
          keys[key] = true
          name = current_name or
            raise ParseError,
                  "#{chunk.source.location}: cannot determine the signature name to bind method attribute #{token.inspect} to"
          (since_until[name] ||= {})[key] = value
        end
      end
    else
      break
    end
  end
  return MethodAttributes.new([], {}) if per_sig.empty?
  sets = per_sig.map {|a| a.uniq.sort }
  unless sets.uniq.size == 1
    raise ParseError,
          "#{chunk.source.location}: method attributes must be the same on every signature of an entry: #{sets.inspect}"
  end
  # 紐付け先の名前がエントリの names と食い違ったら黙って捨てずに
  # エラーにする(名前導出の規約ずれを CI で検出するための安全網)
  since_until.each_key do |name|
    unless chunk.names.include?(name)
      raise ParseError,
            "#{chunk.source.location}: since/until attribute bound to unknown signature name #{name.inspect} (entry names: #{chunk.names.join(', ')})"
    end
  end
  MethodAttributes.new(sets.first || raise, since_until)
end

#method_id(chunk) ⇒ Object



632
633
634
635
636
637
638
639
# File 'lib/bitclust/rrdparser.rb', line 632

def method_id(chunk)
  id = MethodID.new
  id.library = @library
  id.klass   = chunk.signature.klass ? @db.get_class(chunk.signature.klass) : (@klass || raise)
  id.type    = chunk.signature.typename || @type
  id.name    = chunk.names.sort.first
  id
end

#module_functionObject



461
462
463
# File 'lib/bitclust/rrdparser.rb', line 461

def module_function
  @type = :module_function
end

#redefine_class(name) ⇒ Object



426
427
428
429
430
# File 'lib/bitclust/rrdparser.rb', line 426

def redefine_class(name)
  @kind = :redefined
  @klass = name ? @db.get_class(name) : nil
  clear_scope
end

#reopen_class(name) ⇒ Object



420
421
422
423
424
# File 'lib/bitclust/rrdparser.rb', line 420

def reopen_class(name)
  @kind = :added
  @klass = name ? @db.get_class(name) : nil
  clear_scope
end

#require(libname) ⇒ Object



371
372
373
# File 'lib/bitclust/rrdparser.rb', line 371

def require(libname)
  @library.require @db.get_library(libname)
end

#signatureObject



476
477
478
479
# File 'lib/bitclust/rrdparser.rb', line 476

def signature
  return nil unless @klass
  Signature.new(@klass&.name, @type ? typename2mark(@type || raise) : nil, nil)
end

#special_variableObject



469
470
471
472
473
474
# File 'lib/bitclust/rrdparser.rb', line 469

def special_variable
  unless @klass and @klass&.name == 'Kernel'
    raise "must not happen: type=special_variable but class!=Kernel"
  end
  @type = :special_variable
end

#sublibrary(libname) ⇒ Object



375
376
377
# File 'lib/bitclust/rrdparser.rb', line 375

def sublibrary(libname)
  @library.sublibrary @db.get_library(libname)
end