Skip to content

Commit

Permalink
Tentative support of hash splat { **expr }
Browse files Browse the repository at this point in the history
  • Loading branch information
mame committed Aug 7, 2024
1 parent 09a550b commit 6a4e096
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 7 deletions.
1 change: 1 addition & 0 deletions lib/typeprof/core/ast/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def diff(prev_node)
subnode = [subnode] if subnode.is_a?(AST::Node)
prev_subnode = [prev_subnode] if prev_subnode.is_a?(AST::Node)
subnode.zip(prev_subnode) do |subnode0, prev_subnode0|
next if subnode0 == nil && prev_subnode0 == nil
subnode0.diff(prev_subnode0)
return unless subnode0.prev_node
end
Expand Down
22 changes: 16 additions & 6 deletions lib/typeprof/core/ast/value.rb
Original file line number Diff line number Diff line change
Expand Up @@ -246,23 +246,28 @@ def initialize(raw_node, lenv, keywords)
@keys = []
@vals = []
@keywords = keywords
@splat = false

raw_node.elements.each do |raw_elem|
# TODO: Support :assoc_splat_node
case raw_elem.type
when :assoc_node
@keys << AST.create_node(raw_elem.key, lenv)
@vals << AST.create_node(raw_elem.value, lenv)
when :assoc_splat_node
@keys << nil
@vals << AST.create_node(raw_elem.value, lenv)
@splat = true
else
raise "unknown hash elem"
raise "unknown hash elem: #{ raw_elem.type }"
end
end
end

attr_reader :keys, :vals, :keywords
attr_reader :keys, :vals, :splat, :keywords

def subnodes = { keys:, vals: }
def attrs = { keywords: }
def attrs = { splat:, keywords: }

def install0(genv)
unified_key = Vertex.new(self)
Expand All @@ -276,11 +281,16 @@ def install0(genv)
@changes.add_edge(genv, v, unified_val)
literal_pairs[key.lit] = v if key.is_a?(SymbolNode)
else
_h = val.install(genv)
# TODO: if h is a hash, we need to connect its elements to the new hash
h = val.install(genv)
# TODO: do we want to call to_hash on h?
@changes.add_hash_splat_box(genv, h, unified_key, unified_val)
end
end
Source.new(Type::Hash.new(genv, literal_pairs, genv.gen_hash_type(unified_key, unified_val)))
if @splat
Source.new(genv.gen_hash_type(unified_key, unified_val))
else
Source.new(Type::Hash.new(genv, literal_pairs, genv.gen_hash_type(unified_key, unified_val)))
end
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/typeprof/core/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def initialize

attr_reader :type_table

attr_reader :mod_object, :mod_ary, :mod_range
attr_reader :mod_object, :mod_ary, :mod_hash, :mod_range
attr_reader :cls_type, :mod_type
attr_reader :obj_type, :nil_type, :true_type, :false_type, :str_type, :int_type, :float_type
attr_reader :proc_type, :symbol_type, :set_type, :regexp_type, :complex_type
Expand Down
26 changes: 26 additions & 0 deletions lib/typeprof/core/graph/box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,32 @@ def run0(genv, changes)
end
end

class HashSplatBox < Box
def initialize(node, genv, hsh, unified_key, unified_val)
super(node)
@hsh = hsh
@unified_key = unified_key
@unified_val = unified_val
@hsh.add_edge(genv, self)
end

def ret = @hsh # dummy

attr_reader :hsh, :unified_key, :unified_val

def run0(genv, changes)
@hsh.each_type do |ty|
ty = ty.base_type(genv)
if ty.mod == genv.mod_hash
changes.add_edge(genv, ty.args[0], @unified_key)
changes.add_edge(genv, ty.args[1], @unified_val)
else
"???"
end
end
end
end

class MethodDefBox < Box
def initialize(node, genv, cpath, singleton, mid, f_args, ret_boxes)
super(node)
Expand Down
6 changes: 6 additions & 0 deletions lib/typeprof/core/graph/change_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ def add_splat_box(genv, arg)
@new_boxes[key] = SplatBox.new(@node, genv, arg)
end

def add_hash_splat_box(genv, arg, unified_key, unified_val)
key = [:hash_splat, arg, unified_key, unified_val]
return if @new_boxes[key]
@new_boxes[key] = HashSplatBox.new(@node, genv, arg, unified_key, unified_val)
end

def add_masgn_box(genv, rhs, lhss)
key = [:masgn, rhs, lhss]
return if @new_boxes[key]
Expand Down
14 changes: 14 additions & 0 deletions scenario/hash/hash-splat.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## update
def foo
{ **bar, b: 1 }
end

def bar
{ a: 1 }
end

## assert
class Object
def foo: -> Hash[:a | :b, Integer]
def bar: -> Hash[:a, Integer]
end

0 comments on commit 6a4e096

Please sign in to comment.