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)
  "    if \#{node_var}.nil?\n      \#{miss_action}\n    end\n    unless \#{node_var}.is_a?(Array)\n      raise TypeError, \"Expected Array at '\#{path_key}' (\#{mode}); got \\\#{\#{node_var}.class}\"\n    end\n  RB\nend\n".chomp

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

.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?
        "          if block\n            block.call(nil, idx_vec.dup)\n            next\n          else\n            out << [nil, idx_vec.dup]\n            next\n          end\n        RB\n      else\n        <<~RB.chomp\n          if block\n            block.call(nil, idx_vec.dup)\n            return nil\n          else\n            out << [nil, idx_vec.dup]\n            return out\n          end\n        RB\n      end\n    else # :materialize, :read\n      # Important: for :materialize this is ALWAYS nil (never [])\n      return_val = \"nil\"\n      map_depth.positive? ? \"next \#{return_val}\" : \"return \#{return_val}\"\n    end\n  when :skip\n    if mode == :materialize\n      return_val = preview_array ? \"[]\" : \"nil\"\n      map_depth.positive? ? \"next \#{return_val}\" : \"return \#{return_val}\"\n    elsif map_depth.positive?\n      \"next\"\n    else\n      (mode == :each_indexed ? \"if block; return nil; else; return out; end\" : \"return out\")\n    end\n  else # :error\n    %(raise KeyError, \"Missing key '\#{key}' at '\#{path_key}' (\#{mode})\")\n  end\nend\n".chomp

.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
      "        next_node =\n          if \#{node_var}.key?(\#{str}); \#{node_var}[\#{str}]\n          elsif \#{node_var}.key?(\#{sym}); \#{node_var}[\#{sym}]\n          elsif \#{node_var}.key?(\#{str}); \#{node_var}[\#{str}] # (string twice ok / predictable)\n          else :__missing__\n          end\n      RB\n    end\n\n  miss_action = build_miss_action(policy, mode, map_depth, preview_array, key: key, path_key: path_key)\n\n  <<~RB.chomp\n    raise TypeError, \"Expected Hash at '\#{path_key}' (\#{mode})\" unless \#{node_var}.is_a?(Hash)\n    \#{fetch}\n    if next_node == :__missing__\n      \#{miss_action}\n    end\n    \#{node_var} = next_node\n  RB\nend\n".chomp

.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