Class: Typecheck::SignatureCompiler

Inherits:
Object
  • Object
show all
Defined in:
lib/typecheck.rb

Instance Method Summary collapse

Instance Method Details

#call(signature, method) ⇒ Object



10
11
12
13
14
15
16
17
18
19
# File 'lib/typecheck.rb', line 10

def call(signature, method)
  unchecked_method = "#{method}_unchecked"
  check_args, check_out = parse_sig(signature)
  ->(*args) do
    check_args.(*args)
    __send__(unchecked_method, *args).tap do |result|
      check_out.(result)
    end
  end
end

#check_array(type, array) ⇒ Object



78
79
80
# File 'lib/typecheck.rb', line 78

def check_array(type, array)
  array.all? {|element| check_class(type, element) }
end

#check_class(klz, value) ⇒ Object



87
88
89
# File 'lib/typecheck.rb', line 87

def check_class(klz, value)
  value.is_a? klz
end

#check_respond_to(method, value) ⇒ Object



69
70
71
# File 'lib/typecheck.rb', line 69

def check_respond_to(method, value)
  value.respond_to?(method)
end

#parse_sig(sig) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/typecheck.rb', line 21

def parse_sig(sig)
  in_t, out_t = sig.split('->').map(&:strip)
  in_ts = in_t.split(',')

  in_checks = in_ts.map(&method(:parse_type_list))
  out_check = parse_type_list(out_t)

  args_checker = ->(*args) {
    args.each.with_index do |arg, idx|
      in_checks[idx].(arg)
    end
  }
  [args_checker, out_check]
end

#parse_type(subtype, check_or_raise) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/typecheck.rb', line 56

def parse_type(subtype, check_or_raise)
  pred_and subtype.split(';').map(&:strip).map { |type|
    case type
    when /#(.*)/
      method("#{check_or_raise}_respond_to").to_proc.curry.(type[1..-1])
    when /\[(.*)\]/
      method("#{check_or_raise}_array").to_proc.curry.(eval(type[1..-2]))
    else
      method("#{check_or_raise}_class").to_proc.curry.(eval(type))
    end
  }
end

#parse_type_list(choices) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/typecheck.rb', line 44

def parse_type_list(choices)
  types = choices.split('|')
  pred_or [
    pred_or(types.map {|choice|
        parse_type(choice, :check)
      }),
    pred_or(types.map {|choice|
        parse_type(choice, :raise)
      })
  ]
end

#pred_and(preds) ⇒ Object



40
41
42
# File 'lib/typecheck.rb', line 40

def pred_and(preds)
  ->(value) { preds.all?{|pred| pred.(value) } }
end

#pred_or(preds) ⇒ Object



36
37
38
# File 'lib/typecheck.rb', line 36

def pred_or(preds)
  ->(value) { preds.any?{|pred| pred.(value) } }
end

#raise_array(type, array) ⇒ Object

Raises:



82
83
84
85
# File 'lib/typecheck.rb', line 82

def raise_array(type, array)
  raise TypeError, "Bad type: expected #{array} to only contain #{type}" unless check_array(type, array)
  true
end

#raise_class(klz, value) ⇒ Object

Raises:



91
92
93
94
# File 'lib/typecheck.rb', line 91

def raise_class(klz, value)
  raise TypeError, "Bad type: #{value.inspect}, expected #{klz}" unless check_class(klz, value)
  true
end

#raise_respond_to(method, value) ⇒ Object

Raises:



73
74
75
76
# File 'lib/typecheck.rb', line 73

def raise_respond_to(method, value)
  raise TypeError, "Expected #{value.inspect}, to respond_to #{method}" unless check_respond_to(method, value)
  true
end