Module: Kumi::Core::Compiler::AccessEmit::Base

Included in:
EachIndexed, Materialize, Ravel, Read
Defined in:
lib/kumi/core/compiler/access_emit/base.rb

Class Method Summary collapse

Class Method Details

.array_guard_code(node_var:, mode:, policy:, path_key:, map_depth:) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/kumi/core/compiler/access_emit/base.rb', line 68

def array_guard_code(node_var:, mode:, policy:, path_key:, map_depth:)
  miss_action = build_array_miss_action(policy, mode, map_depth, path_key)
  <<~RB.chomp
    if #{node_var}.nil?
      #{miss_action}
    end
    unless #{node_var}.is_a?(Array)
      raise TypeError, "Expected Array at '#{path_key}' (#{mode}); got \#{#{node_var}.class}"
    end
  RB
end

.build_array_miss_action(policy, mode, map_depth, path_key) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/kumi/core/compiler/access_emit/base.rb', line 129

def build_array_miss_action(policy, mode, map_depth, path_key)
  case policy
  when :nil
    if mode == :materialize
      map_depth.positive? ? "next nil" : "return nil"
    elsif mode == :each_indexed
      if map_depth.positive?
        <<~RB.chomp
          if block
            block.call(nil, idx_vec.dup)
            next
          else
            out << [nil, idx_vec.dup]
            next
          end
        RB
      else
        <<~RB.chomp
          if block
            block.call(nil, idx_vec.dup)
            return nil
          else
            out << [nil, idx_vec.dup]
            return out
          end
        RB
      end
    else # :ravel / others
      base = "out << nil"
      cont = map_depth.positive? ? "next" : "return out"
      "#{base}\n#{cont}"
    end
  when :skip
    if mode == :materialize
      map_depth.positive? ? "next []" : "return []"
    elsif mode == :each_indexed
      map_depth.positive? ? "next" : "if block; return nil; else; return out; end"
    else # :ravel
      map_depth.positive? ? "next" : "return out"
    end
  else
    %(raise TypeError, "Missing array at '#{path_key}' (#{mode})")
  end
end

.build_miss_action(policy, mode, map_depth, preview_array, key:, path_key:) ⇒ Object

———- missing behaviors ———-



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
# File 'lib/kumi/core/compiler/access_emit/base.rb', line 81

def build_miss_action(policy, mode, map_depth, preview_array, key:, path_key:)
  case policy
  when :nil
    if mode == :ravel
      base = "out << nil"
      cont = map_depth.positive? ? "next" : "return out"
      "#{base}\n#{cont}"
    elsif mode == :each_indexed
      if map_depth.positive?
        <<~RB.chomp
          if block
            block.call(nil, idx_vec.dup)
            next
          else
            out << [nil, idx_vec.dup]
            next
          end
        RB
      else
        <<~RB.chomp
          if block
            block.call(nil, idx_vec.dup)
            return nil
          else
            out << [nil, idx_vec.dup]
            return out
          end
        RB
      end
    else # :materialize, :read
      # Important: for :materialize this is ALWAYS nil (never [])
      return_val = "nil"
      map_depth.positive? ? "next #{return_val}" : "return #{return_val}"
    end
  when :skip
    if mode == :materialize
      return_val = preview_array ? "[]" : "nil"
      map_depth.positive? ? "next #{return_val}" : "return #{return_val}"
    elsif map_depth.positive?
      "next"
    else
      (mode == :each_indexed ? "if block; return nil; else; return out; end" : "return out")
    end
  else # :error
    %(raise KeyError, "Missing key '#{key}' at '#{path_key}' (#{mode})")
  end
end

.fetch_hash_code(node_var:, key:, key_policy:, preview_array:, mode:, policy:, path_key:, map_depth:) ⇒ Object

———- codegen helpers ———-



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
# File 'lib/kumi/core/compiler/access_emit/base.rb', line 34

def fetch_hash_code(node_var:, key:, key_policy:, preview_array:, mode:, policy:, path_key:, map_depth:)
  effective_policy = preview_array ? :indifferent : (key_policy || :indifferent)
  str = key.to_s.inspect
  sym = key.to_sym.inspect

  fetch =
    case effective_policy
    when :string
      %(next_node = #{node_var}.key?(#{str}) ? #{node_var}[#{str}] : :__missing__)
    when :symbol
      %(next_node = #{node_var}.key?(#{sym}) ? #{node_var}[#{sym}] : :__missing__)
    else # :indifferent
      <<~RB.chomp
        next_node =
          if #{node_var}.key?(#{str}); #{node_var}[#{str}]
          elsif #{node_var}.key?(#{sym}); #{node_var}[#{sym}]
          elsif #{node_var}.key?(#{str}); #{node_var}[#{str}] # (string twice ok / predictable)
          else :__missing__
          end
      RB
    end

  miss_action = build_miss_action(policy, mode, map_depth, preview_array, key: key, path_key: path_key)

  <<~RB.chomp
    raise TypeError, "Expected Hash at '#{path_key}' (#{mode})" unless #{node_var}.is_a?(Hash)
    #{fetch}
    if next_node == :__missing__
      #{miss_action}
    end
    #{node_var} = next_node
  RB
end

.segment_ops(ops) ⇒ Object

———- IR segmentation ———-



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/kumi/core/compiler/access_emit/base.rb', line 11

def segment_ops(ops)
  segs = []
  cur = []
  i = 0
  while i < ops.length
    case ops[i][:type]
    when :enter_hash
      preview = (i + 1 < ops.length) && ops[i + 1][:type] == :enter_array
      cur << [:enter_hash, ops[i][:key].to_s, preview]
    when :enter_array
      segs << cur unless cur.empty?
      segs << :array
      cur = []
    else
      raise "Unknown operation: #{ops[i].inspect}"
    end
    i += 1
  end
  segs << cur unless cur.empty?
  segs
end