File: interface.rb

package info (click to toggle)
ruby-dry-logic 1.2.0-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 728 kB
  • sloc: ruby: 4,929; makefile: 6
file content (141 lines) | stat: -rw-r--r-- 3,145 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# frozen_string_literal: true

module Dry
  module Logic
    class Rule
      class Interface < ::Module
        SPLAT = ["*rest"].freeze

        attr_reader :arity

        attr_reader :curried

        def initialize(arity, curried)
          @arity = arity
          @curried = curried

          if !variable_arity? && curried > arity
            raise ArgumentError, "wrong number of arguments (#{curried} for #{arity})"
          end

          define_constructor if curried?

          if constant?
            define_constant_application
          else
            define_application
          end
        end

        def constant?
          arity.zero?
        end

        def variable_arity?
          arity.negative?
        end

        def curried?
          !curried.zero?
        end

        def unapplied
          if variable_arity?
            unapplied = arity.abs - 1 - curried

            if unapplied.negative?
              0
            else
              unapplied
            end
          else
            arity - curried
          end
        end

        def name
          if constant?
            "Constant"
          else
            arity_str =
              if variable_arity?
                "Variable#{arity.abs - 1}Arity"
              else
                "#{arity}Arity"
              end

            curried_str =
              if curried?
                "#{curried}Curried"
              else
                EMPTY_STRING
              end

            "#{arity_str}#{curried_str}"
          end
        end

        def define_constructor
          assignment =
            if curried.equal?(1)
              "@arg0 = @args[0]"
            else
              "#{curried_args.join(", ")} = @args"
            end

          module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
            def initialize(*)
              super

              #{assignment}
            end
          RUBY
        end

        def define_constant_application
          module_exec do
            def call(*)
              if @predicate[]
                Result::SUCCESS
              else
                Result.new(false, id) { ast }
              end
            end

            def [](*)
              @predicate[]
            end
          end
        end

        def define_application
          splat = variable_arity? ? SPLAT : EMPTY_ARRAY
          parameters = (unapplied_args + splat).join(", ")
          application = "@predicate[#{(curried_args + unapplied_args + splat).join(", ")}]"

          module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
            def call(#{parameters})
              if #{application}
                Result::SUCCESS
              else
                Result.new(false, id) { ast(#{parameters}) }
              end
            end

            def [](#{parameters})
              #{application}
            end
          RUBY
        end

        def curried_args
          @curried_args ||= ::Array.new(curried) { |i| "@arg#{i}" }
        end

        def unapplied_args
          @unapplied_args ||= ::Array.new(unapplied) { |i| "input#{i}" }
        end
      end
    end
  end
end