raap 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/CHANGELOG.md +7 -0
 - data/CODE_OF_CONDUCT.md +84 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +109 -0
 - data/Rakefile +8 -0
 - data/Steepfile +4 -0
 - data/exe/raap +3 -0
 - data/lib/raap/bind_call.rb +29 -0
 - data/lib/raap/cli.rb +239 -0
 - data/lib/raap/function_type.rb +63 -0
 - data/lib/raap/method_property.rb +122 -0
 - data/lib/raap/method_type.rb +40 -0
 - data/lib/raap/method_value.rb +38 -0
 - data/lib/raap/rbs.rb +23 -0
 - data/lib/raap/result.rb +18 -0
 - data/lib/raap/sized.rb +37 -0
 - data/lib/raap/symbolic_caller.rb +128 -0
 - data/lib/raap/type.rb +310 -0
 - data/lib/raap/type_substitution.rb +53 -0
 - data/lib/raap/value/bottom.rb +8 -0
 - data/lib/raap/value/interface.rb +50 -0
 - data/lib/raap/value/intersection.rb +35 -0
 - data/lib/raap/value/module.rb +17 -0
 - data/lib/raap/value/top.rb +8 -0
 - data/lib/raap/value/variable.rb +14 -0
 - data/lib/raap/value/void.rb +8 -0
 - data/lib/raap/value.rb +13 -0
 - data/lib/raap/version.rb +5 -0
 - data/lib/raap.rb +34 -0
 - data/rbs_collection.lock.yaml +68 -0
 - data/rbs_collection.yaml +20 -0
 - data/sig/raap.rbs +313 -0
 - metadata +107 -0
 
| 
         @@ -0,0 +1,122 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RaaP
         
     | 
| 
      
 4 
     | 
    
         
            +
              class MethodProperty
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Stats < Struct.new(:success, :skip, :exception)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(success: 0, skip: 0, exception: 0)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    super
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                attr_reader :receiver_type
         
     | 
| 
      
 12 
     | 
    
         
            +
                attr_reader :method_name
         
     | 
| 
      
 13 
     | 
    
         
            +
                attr_reader :method_type
         
     | 
| 
      
 14 
     | 
    
         
            +
                attr_reader :size_step
         
     | 
| 
      
 15 
     | 
    
         
            +
                attr_reader :timeout
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def initialize(receiver_type:, method_name:, method_type:, size_step:, timeout:)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @receiver_type = receiver_type
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @method_name = method_name
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @method_type = method_type
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @size_step = size_step
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @timeout = timeout
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def run
         
     | 
| 
      
 26 
     | 
    
         
            +
                  stats = Stats.new
         
     | 
| 
      
 27 
     | 
    
         
            +
                  if receiver_type.type.to_s == 'String' && method_name == :initialize
         
     | 
| 
      
 28 
     | 
    
         
            +
                    return stats
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 31 
     | 
    
         
            +
                    Timeout.timeout(@timeout) do
         
     | 
| 
      
 32 
     | 
    
         
            +
                      catch(:break) do
         
     | 
| 
      
 33 
     | 
    
         
            +
                        @size_step.each do |size|
         
     | 
| 
      
 34 
     | 
    
         
            +
                          yield call(size: size, stats: stats)
         
     | 
| 
      
 35 
     | 
    
         
            +
                        end
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  rescue Timeout::Error => exception
         
     | 
| 
      
 39 
     | 
    
         
            +
                    RaaP.logger.warn "Timeout: #{exception}"
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  stats
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                private
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def call(size:, stats:)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  receiver_value = receiver_type.pick(size: size, eval: false)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  arguments = method_type.pick_arguments(size: size, eval: false)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  method_value = MethodValue.new(receiver_value:, arguments:, method_name:, size:)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  symbolic_call = method_value.to_symbolic_call
         
     | 
| 
      
 51 
     | 
    
         
            +
                  check = false
         
     | 
| 
      
 52 
     | 
    
         
            +
                  if return_type.instance_of?(::RBS::Types::Bases::Bottom)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 54 
     | 
    
         
            +
                      return_value = SymbolicCaller.new(symbolic_call).eval
         
     | 
| 
      
 55 
     | 
    
         
            +
                    rescue => e
         
     | 
| 
      
 56 
     | 
    
         
            +
                      check = true
         
     | 
| 
      
 57 
     | 
    
         
            +
                      return_value = Value::Bottom.new
         
     | 
| 
      
 58 
     | 
    
         
            +
                    end
         
     | 
| 
      
 59 
     | 
    
         
            +
                  else
         
     | 
| 
      
 60 
     | 
    
         
            +
                    return_value = SymbolicCaller.new(symbolic_call).eval
         
     | 
| 
      
 61 
     | 
    
         
            +
                    check = check_return(receiver_value:, return_value:, method_type:)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  if check
         
     | 
| 
      
 64 
     | 
    
         
            +
                    stats.success += 1
         
     | 
| 
      
 65 
     | 
    
         
            +
                    Result::Success.new(method_value:, return_value:)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  else
         
     | 
| 
      
 67 
     | 
    
         
            +
                    Result::Failure.new(method_value:, return_value:, symbolic_call:)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                rescue NoMethodError => exception
         
     | 
| 
      
 70 
     | 
    
         
            +
                  stats.skip += 1
         
     | 
| 
      
 71 
     | 
    
         
            +
                  Result::Skip.new(method_value:, exception:)
         
     | 
| 
      
 72 
     | 
    
         
            +
                rescue NameError => e
         
     | 
| 
      
 73 
     | 
    
         
            +
                  msg = e.name.nil? ? '' : "for `#{BindCall.to_s(e.receiver)}::#{e.name}`"
         
     | 
| 
      
 74 
     | 
    
         
            +
                  RaaP.logger.error("Implementation is not found #{msg} maybe.")
         
     | 
| 
      
 75 
     | 
    
         
            +
                  throw :break
         
     | 
| 
      
 76 
     | 
    
         
            +
                rescue NotImplementedError => exception
         
     | 
| 
      
 77 
     | 
    
         
            +
                  stats.skip += 1
         
     | 
| 
      
 78 
     | 
    
         
            +
                  Result::Skip.new(method_value:, exception:)
         
     | 
| 
      
 79 
     | 
    
         
            +
                rescue SystemStackError => exception
         
     | 
| 
      
 80 
     | 
    
         
            +
                  stats.skip += 1
         
     | 
| 
      
 81 
     | 
    
         
            +
                  RaaP.logger.warn "Found recursive type definition."
         
     | 
| 
      
 82 
     | 
    
         
            +
                  Result::Skip.new(method_value: nil, exception:)
         
     | 
| 
      
 83 
     | 
    
         
            +
                rescue TypeError => exception
         
     | 
| 
      
 84 
     | 
    
         
            +
                  Result::Failure.new(method_value:, return_value:, symbolic_call:)
         
     | 
| 
      
 85 
     | 
    
         
            +
                rescue => exception
         
     | 
| 
      
 86 
     | 
    
         
            +
                  stats.exception += 1
         
     | 
| 
      
 87 
     | 
    
         
            +
                  Result::Exception.new(method_value:, exception:)
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                def check_return(receiver_value:, return_value:, method_type:)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  if BindCall.is_a?(receiver_value, Module)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    if BindCall.is_a?(return_type, ::RBS::Types::ClassSingleton)
         
     | 
| 
      
 93 
     | 
    
         
            +
                      # ::RBS::Test::TypeCheck cannot support to check singleton class
         
     | 
| 
      
 94 
     | 
    
         
            +
                      return receiver_value == return_value
         
     | 
| 
      
 95 
     | 
    
         
            +
                    end
         
     | 
| 
      
 96 
     | 
    
         
            +
                    self_class = receiver_value
         
     | 
| 
      
 97 
     | 
    
         
            +
                    instance_class = receiver_value
         
     | 
| 
      
 98 
     | 
    
         
            +
                  else
         
     | 
| 
      
 99 
     | 
    
         
            +
                    self_class = BindCall.class(receiver_value)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    instance_class = BindCall.class(receiver_value)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
                  type_check = ::RBS::Test::TypeCheck.new(
         
     | 
| 
      
 103 
     | 
    
         
            +
                    self_class: self_class,
         
     | 
| 
      
 104 
     | 
    
         
            +
                    instance_class: instance_class,
         
     | 
| 
      
 105 
     | 
    
         
            +
                    class_class: Module,
         
     | 
| 
      
 106 
     | 
    
         
            +
                    builder: RBS.builder,
         
     | 
| 
      
 107 
     | 
    
         
            +
                    sample_size: 1,
         
     | 
| 
      
 108 
     | 
    
         
            +
                    unchecked_classes: []
         
     | 
| 
      
 109 
     | 
    
         
            +
                  )
         
     | 
| 
      
 110 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 111 
     | 
    
         
            +
                    type_check.value(return_value, return_type)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  rescue => e
         
     | 
| 
      
 113 
     | 
    
         
            +
                    $stderr.puts "Type check fail by `(#{e.class}) #{e.message}`"
         
     | 
| 
      
 114 
     | 
    
         
            +
                    false
         
     | 
| 
      
 115 
     | 
    
         
            +
                  end
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                def return_type
         
     | 
| 
      
 119 
     | 
    
         
            +
                  method_type.rbs.type.return_type
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
              end
         
     | 
| 
      
 122 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RaaP
         
     | 
| 
      
 4 
     | 
    
         
            +
              class MethodType
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :rbs
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(method, type_params_decl: [], type_args: [], self_type: nil, instance_type: nil, class_type: nil)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  rbs =
         
     | 
| 
      
 9 
     | 
    
         
            +
                    case method
         
     | 
| 
      
 10 
     | 
    
         
            +
                    when ""
         
     | 
| 
      
 11 
     | 
    
         
            +
                      raise ArgumentError, "method type is empty"
         
     | 
| 
      
 12 
     | 
    
         
            +
                    when String
         
     | 
| 
      
 13 
     | 
    
         
            +
                      ::RBS::Parser.parse_method_type(method, require_eof: true) or raise
         
     | 
| 
      
 14 
     | 
    
         
            +
                    when ::RBS::MethodType
         
     | 
| 
      
 15 
     | 
    
         
            +
                      method
         
     | 
| 
      
 16 
     | 
    
         
            +
                    else
         
     | 
| 
      
 17 
     | 
    
         
            +
                      raise "bad method #{method}"
         
     | 
| 
      
 18 
     | 
    
         
            +
                    end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  ts = TypeSubstitution.new(type_params_decl + rbs.type_params, type_args)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  @rbs = ts.method_type_sub(rbs, self_type:, instance_type:, class_type:)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @fun_type = FunctionType.new(@rbs.type)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def pick_arguments(size: 10, eval: true)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  args, kwargs = @fun_type.pick_arguments(size: size, eval:)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  block = pick_block(size: size, eval:)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  [args, kwargs, block]
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def pick_block(size: 10, eval: true)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  block = @rbs.block
         
     | 
| 
      
 34 
     | 
    
         
            +
                  return nil if block.nil?
         
     | 
| 
      
 35 
     | 
    
         
            +
                  return nil if (block.required == false) && [true, false].sample
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  Proc.new { Type.new(block.type.return_type).pick(size:, eval:) }
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RaaP
         
     | 
| 
      
 4 
     | 
    
         
            +
              class MethodValue < Data.define(
         
     | 
| 
      
 5 
     | 
    
         
            +
                :receiver_value,
         
     | 
| 
      
 6 
     | 
    
         
            +
                :arguments,
         
     | 
| 
      
 7 
     | 
    
         
            +
                :method_name,
         
     | 
| 
      
 8 
     | 
    
         
            +
                :size
         
     | 
| 
      
 9 
     | 
    
         
            +
              )
         
     | 
| 
      
 10 
     | 
    
         
            +
                def to_symbolic_call
         
     | 
| 
      
 11 
     | 
    
         
            +
                  args, kwargs, block = arguments
         
     | 
| 
      
 12 
     | 
    
         
            +
                  [:call, receiver_value, method_name, args, kwargs, block]
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def call_str
         
     | 
| 
      
 16 
     | 
    
         
            +
                  r = SymbolicCaller.new(receiver_value).eval
         
     | 
| 
      
 17 
     | 
    
         
            +
                  "#{BindCall.inspect(r)}.#{method_name}(#{argument_str})#{block_str}"
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                private
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def argument_str
         
     | 
| 
      
 23 
     | 
    
         
            +
                  args, kwargs, _ = SymbolicCaller.new(arguments).eval
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  r = []
         
     | 
| 
      
 26 
     | 
    
         
            +
                  r << args.map(&:inspect).join(', ') if !args.empty?
         
     | 
| 
      
 27 
     | 
    
         
            +
                  r << kwargs.map { |k ,v| "#{k}: #{BindCall.inspect(v)}" }.join(', ') if !kwargs.empty?
         
     | 
| 
      
 28 
     | 
    
         
            +
                  r.join(', ')
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def block_str
         
     | 
| 
      
 32 
     | 
    
         
            +
                  _, _, block = SymbolicCaller.new(arguments).eval
         
     | 
| 
      
 33 
     | 
    
         
            +
                  if block
         
     | 
| 
      
 34 
     | 
    
         
            +
                    "{ }"
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/raap/rbs.rb
    ADDED
    
    | 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RaaP
         
     | 
| 
      
 4 
     | 
    
         
            +
              module RBS
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.builder
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @builder ||= ::RBS::DefinitionBuilder.new(env: env.resolve_type_names)
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def self.env
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @env ||= ::RBS::Environment.from_loader(loader)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def self.loader
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @loader ||= ::RBS::CLI::LibraryOptions.new.loader
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def self.parse_type(type)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  raise ArgumentError, "empty type" if type == ""
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  ::RBS::Parser.parse_type(type, require_eof: true) or raise
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/raap/result.rb
    ADDED
    
    | 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RaaP
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Result
         
     | 
| 
      
 5 
     | 
    
         
            +
                module CalledStr
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def called_str
         
     | 
| 
      
 7 
     | 
    
         
            +
                    "#{method_value.call_str} -> #{return_value.inspect}[#{return_value.class}]"
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                Success = Data.define(:method_value, :return_value)
         
     | 
| 
      
 12 
     | 
    
         
            +
                Success.include CalledStr
         
     | 
| 
      
 13 
     | 
    
         
            +
                Failure = Data.define(:method_value, :return_value, :symbolic_call)
         
     | 
| 
      
 14 
     | 
    
         
            +
                Failure.include CalledStr
         
     | 
| 
      
 15 
     | 
    
         
            +
                Skip = Data.define(:method_value, :exception)
         
     | 
| 
      
 16 
     | 
    
         
            +
                Exception = Data.define(:method_value, :exception)
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/raap/sized.rb
    ADDED
    
    | 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RaaP
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Sized
         
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(&block)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  raise LocalJumpError, "no block given" unless block
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @block = block
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @such_that = nil
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def pick(size:)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  such_that_loop do |skip|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @block.call(size + skip)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def such_that(&block)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @such_that = block
         
     | 
| 
      
 19 
     | 
    
         
            +
                  self
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
                alias when such_that
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                private
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def such_that_loop
         
     | 
| 
      
 26 
     | 
    
         
            +
                  skip = 0
         
     | 
| 
      
 27 
     | 
    
         
            +
                  while skip < 100
         
     | 
| 
      
 28 
     | 
    
         
            +
                    picked = yield(skip)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    such_that = @such_that
         
     | 
| 
      
 30 
     | 
    
         
            +
                    return picked if such_that.nil? || such_that.call(picked)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    skip += 1
         
     | 
| 
      
 32 
     | 
    
         
            +
                    raise "too many skips" unless skip < 100
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  raise "never reached"
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,128 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RaaP
         
     | 
| 
      
 4 
     | 
    
         
            +
              # sc = SymbolicCaller.new(
         
     | 
| 
      
 5 
     | 
    
         
            +
              #   [:call,
         
     | 
| 
      
 6 
     | 
    
         
            +
              #     [:call, C, :new, [], {
         
     | 
| 
      
 7 
     | 
    
         
            +
              #       a: [:call, A, :new, [], {}, nil],
         
     | 
| 
      
 8 
     | 
    
         
            +
              #       b: [:call, B, :new, [], {}, nil] }, nil],
         
     | 
| 
      
 9 
     | 
    
         
            +
              #     :run, [], {}, nil]
         
     | 
| 
      
 10 
     | 
    
         
            +
              # sc.eval #=> 123
         
     | 
| 
      
 11 
     | 
    
         
            +
              #
         
     | 
| 
      
 12 
     | 
    
         
            +
              # sc.to_lines
         
     | 
| 
      
 13 
     | 
    
         
            +
              # ↓
         
     | 
| 
      
 14 
     | 
    
         
            +
              # a = A.new(1)
         
     | 
| 
      
 15 
     | 
    
         
            +
              # b = B.new(b: 'bbb')
         
     | 
| 
      
 16 
     | 
    
         
            +
              # c = C.new(a: a, b: b)
         
     | 
| 
      
 17 
     | 
    
         
            +
              # c.run() { }
         
     | 
| 
      
 18 
     | 
    
         
            +
              class SymbolicCaller
         
     | 
| 
      
 19 
     | 
    
         
            +
                attr_reader :symbolic_call
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def initialize(symbolic_call)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @symbolic_call = symbolic_call
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def eval
         
     | 
| 
      
 26 
     | 
    
         
            +
                  walk do |symbolic_call|
         
     | 
| 
      
 27 
     | 
    
         
            +
                    eval_one(symbolic_call)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def walk(&)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  _walk(@symbolic_call, &)
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def to_lines
         
     | 
| 
      
 36 
     | 
    
         
            +
                  [].tap do |lines|
         
     | 
| 
      
 37 
     | 
    
         
            +
                    walk do |symbolic_call|
         
     | 
| 
      
 38 
     | 
    
         
            +
                      symbolic_call => [:call, receiver_value, method_name, args, kwargs, block]
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                      is_mod = receiver_value.is_a?(Module)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                      if receiver_value == Kernel
         
     | 
| 
      
 43 
     | 
    
         
            +
                        var = "#{var_name(method_name)} = "
         
     | 
| 
      
 44 
     | 
    
         
            +
                        receiver = ''
         
     | 
| 
      
 45 
     | 
    
         
            +
                      elsif is_mod
         
     | 
| 
      
 46 
     | 
    
         
            +
                        var = "#{var_name(receiver_value)} = "
         
     | 
| 
      
 47 
     | 
    
         
            +
                        receiver = receiver_value.name + '.'
         
     | 
| 
      
 48 
     | 
    
         
            +
                      else
         
     | 
| 
      
 49 
     | 
    
         
            +
                        var = ""
         
     | 
| 
      
 50 
     | 
    
         
            +
                        receiver = if printable?(receiver_value)
         
     | 
| 
      
 51 
     | 
    
         
            +
                          printable(receiver_value) + '.'
         
     | 
| 
      
 52 
     | 
    
         
            +
                        else
         
     | 
| 
      
 53 
     | 
    
         
            +
                          var_name(receiver_value.class) + '.'
         
     | 
| 
      
 54 
     | 
    
         
            +
                        end
         
     | 
| 
      
 55 
     | 
    
         
            +
                      end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                      arguments = []
         
     | 
| 
      
 58 
     | 
    
         
            +
                      arguments << args.map { |a| printable(a) } if !args.empty?
         
     | 
| 
      
 59 
     | 
    
         
            +
                      arguments << kwargs.map{|k,v| "#{k}: #{printable(v)}" }.join(', ') if !kwargs.empty?
         
     | 
| 
      
 60 
     | 
    
         
            +
                      block_str = block ? " { }" : ""
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                      lines << "#{var}#{receiver}#{method_name}(#{arguments.join(', ')})#{block_str}"
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                      eval_one(symbolic_call)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                private
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def _walk(symbolic_call, &block)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  return symbolic_call if BindCall::instance_of?(symbolic_call, BasicObject)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  return symbolic_call if !BindCall.respond_to?(symbolic_call, :deconstruct) && !BindCall.respond_to?(symbolic_call, :deconstruct_keys)
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  case symbolic_call
         
     | 
| 
      
 76 
     | 
    
         
            +
                  in [:call, receiver, Symbol => method_name, Array => args, Hash => kwargs, b]
         
     | 
| 
      
 77 
     | 
    
         
            +
                    receiver = _walk(receiver, &block)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    args = _walk(args, &block) if !args.empty?
         
     | 
| 
      
 79 
     | 
    
         
            +
                    kwargs = _walk(kwargs, &block) if !kwargs.empty?
         
     | 
| 
      
 80 
     | 
    
         
            +
                    block.call [:call, receiver, method_name, args, kwargs, b]
         
     | 
| 
      
 81 
     | 
    
         
            +
                  in Array
         
     | 
| 
      
 82 
     | 
    
         
            +
                    symbolic_call.map { |sc| _walk(sc, &block) }
         
     | 
| 
      
 83 
     | 
    
         
            +
                  in Hash
         
     | 
| 
      
 84 
     | 
    
         
            +
                    symbolic_call.transform_values { |sc| _walk(sc, &block) }
         
     | 
| 
      
 85 
     | 
    
         
            +
                  else
         
     | 
| 
      
 86 
     | 
    
         
            +
                    symbolic_call
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                def eval_one(symbolic_call)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  symbolic_call => [:call, receiver_value, method_name, args, kwargs, block]
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 94 
     | 
    
         
            +
                    receiver_value.__send__(method_name, *args, **kwargs, &block)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  rescue => e
         
     | 
| 
      
 96 
     | 
    
         
            +
                    RaaP.logger.error("Cannot eval symbolic call #{symbolic_call} with #{e.class}")
         
     | 
| 
      
 97 
     | 
    
         
            +
                    raise
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                def var_name(mod)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  printable(mod).gsub('::', '_').downcase
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                def printable?(obj)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  case obj
         
     | 
| 
      
 107 
     | 
    
         
            +
                  when Symbol, Integer, Float, Regexp, nil, true, false, String, Module
         
     | 
| 
      
 108 
     | 
    
         
            +
                    true
         
     | 
| 
      
 109 
     | 
    
         
            +
                  else
         
     | 
| 
      
 110 
     | 
    
         
            +
                    false
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                def printable(obj)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  case obj
         
     | 
| 
      
 116 
     | 
    
         
            +
                  # Object from which it can get strings that can be eval with `#inspect`
         
     | 
| 
      
 117 
     | 
    
         
            +
                  when Symbol, Integer, Float, Regexp, nil, true, false
         
     | 
| 
      
 118 
     | 
    
         
            +
                    obj.inspect
         
     | 
| 
      
 119 
     | 
    
         
            +
                  when String
         
     | 
| 
      
 120 
     | 
    
         
            +
                    obj.inspect.gsub('"', "'") or raise
         
     | 
| 
      
 121 
     | 
    
         
            +
                  when Module
         
     | 
| 
      
 122 
     | 
    
         
            +
                    BindCall.name(obj) or raise
         
     | 
| 
      
 123 
     | 
    
         
            +
                  else
         
     | 
| 
      
 124 
     | 
    
         
            +
                    var_name(obj.class)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
              end
         
     | 
| 
      
 128 
     | 
    
         
            +
            end
         
     |