Skip to content

Commit

Permalink
special syntax for Data subclasses
Browse files Browse the repository at this point in the history
allows declaring and typing variables, inject inherited methods OTTB
  • Loading branch information
HoneyryderChuck committed Oct 15, 2024
1 parent 36a3d5f commit bd70d45
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 36 deletions.
9 changes: 8 additions & 1 deletion ext/rbs_extension/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1747,6 +1747,7 @@ VALUE parse_member_def(parserstate *state, bool instance_only, bool accept_overl

/**
* class_instance_name ::= {} <class_name>
* | {} Data `[` kwarg args `]`
* | {} class_name `[` type args <`]`>
*
* @param kind
Expand All @@ -1756,7 +1757,13 @@ void class_instance_name(parserstate *state, TypeNameKind kind, VALUE *name, VAL

*name = parse_type_name(state, kind, name_range);

if (state->next_token.type == pLBRACKET) {
if (CLASS_OF(*name) == RBS_TypeName && rb_funcall(*name, rb_intern("data?"), 0) == Qtrue) {
parser_advance_assert(state, pLPAREN);
args_range->start = state->current_token.range.start;
*args = parse_record_attributes(state);
parser_advance_assert(state, pRPAREN);
args_range->end = state->current_token.range.end;
} else if (state->next_token.type == pLBRACKET) {
parser_advance(state);
args_range->start = state->current_token.range.start;
parse_type_list(state, pRBRACKET, args);
Expand Down
135 changes: 135 additions & 0 deletions lib/rbs/ast/declarations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,141 @@ def to_json(state = _ = nil)
location: location
}.to_json(state)
end

def self.new(name: , args:, location:)

return super unless name.data?

superklass = super(name: name, args: [], location: location)

args.transform_values! do |(type, required)|
required ? type : Types::Optional.new(type: type, location: type.location)
end

# attribute readers
members = args.map do |k, type|
Members::AttrReader.new(
name: k,
type: type,
ivar_name: :"@#{type}",
kind: :instance,
location: location,
comment: nil,
annotations: nil
)
end

# initialize
members << Members::MethodDefinition.new(
name: :initialize,
kind: :instance,
location: location,
overloading: false,
comment: nil,
annotations: nil,
visibility: nil,
overloads: [
Members::MethodDefinition::Overload.new(
method_type: MethodType.new(
type_params: [],
type: Types::Function.new(
required_keywords: args.to_h { |k, type|
[
k,
# set param
Types::Function::Param.new(
name: nil,
type: type,
location: location
)
]
},
required_positionals: [],
optional_keywords: {},
optional_positionals: [],
rest_keywords: nil,
rest_positionals: nil,
trailing_positionals: [],
return_type: RBS::Types::Bases::Void.new(location: location),
),
location: location,
block: nil,
),
annotations: []
),
Members::MethodDefinition::Overload.new(
method_type: MethodType.new(
type_params: [],
type: Types::Function.new(
required_positionals: args.map { |k, type|
# set param
Types::Function::Param.new(
name: k,
type: type,
location: location
)
},
required_keywords: [],
optional_keywords: {},
optional_positionals: [],
rest_keywords: nil,
rest_positionals: nil,
trailing_positionals: [],
return_type: RBS::Types::Bases::Void.new(location: location),
),
location: location,
block: nil,
),
annotations: []
)
]
)

# members
members << Members::MethodDefinition.new(
name: :members,
kind: :instance,
location: location,
overloading: false,
comment: nil,
annotations: nil,
visibility: nil,
overloads: [
Members::MethodDefinition::Overload.new(
method_type: MethodType.new(
type_params: [],
type: Types::Function.new(
required_keywords: {},
required_positionals: [],
optional_keywords: {},
optional_positionals: [],
rest_keywords: nil,
rest_positionals: nil,
trailing_positionals: [],
return_type: RBS::Types::ClassInstance.new(
name: BuiltinNames::Array,
args: [RBS::Types::ClassInstance.new(name: BuiltinNames::Symbol, args: [], location: location)],
location: location
),
),
location: location,
block: nil,
),
annotations: []
)
]
)

Class.new(
name: nil,
type_params: nil,
super_class: superklass,
annotations: nil,
comment: nil,
location: location,
members: members
)
end
end

include NestedDeclarationHelper
Expand Down
4 changes: 4 additions & 0 deletions lib/rbs/type_name.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ def alias?
kind == :alias
end

def data?
class? && namespace.empty? && name == :Data
end

def absolute!
self.class.new(namespace: namespace.absolute!, name: name)
end
Expand Down
79 changes: 44 additions & 35 deletions test/rbs/parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -438,59 +438,68 @@ class Foo < Data(a: Integer)
assert_equal TypeName("Foo"), decl.name
assert_predicate decl.type_params, :empty?
assert_nil decl.super_class.name
assert_equal TypeName("Data"), decl.super_class.superclass.name
assert_equal TypeName("Data"), decl.super_class.super_class.name

assert_equal 8, decl.members.size
members = decl.super_class.members

decl.members[0].tap do |member|
# assert_equal 8, members.size

members[0].tap do |member|
assert_instance_of RBS::AST::Members::AttrReader, member
assert_equal :instance, member.kind
assert_equal :a, member.name
assert_equal "Integer", member.type.to_s
end

decl.members[1].tap do |member|
members[1].tap do |member|
assert_instance_of RBS::AST::Members::MethodDefinition, member
assert_equal :method, member.kind
assert_equal :instance, member.kind
assert_equal :initialize, member.name
assert_equal ["(::Integer) -> void | (id: ::Integer) -> void"], member.method_types.map(&:to_s)
assert_equal 2, member.overloads.size
assert_equal "(a: Integer) -> void", member.overloads[0].method_type.to_s
assert_equal"(Integer a) -> void", member.overloads[1].method_type.to_s
end

decl.members[2].tap do |member|
members[2].tap do |member|
assert_instance_of RBS::AST::Members::MethodDefinition, member
assert_equal :method, member.kind
assert_equal :instance, member.kind
assert_equal :members, member.name
assert_equal ["() -> Array[Symbol]"], member.method_types.map(&:to_s)
end

decl.members[3].tap do |member|
assert_instance_of RBS::AST::Members::MethodDefinition, member
assert_equal :method, member.kind
assert_equal :deconstruct, member.name
assert_equal ["() -> [Integer]"], member.method_types.map(&:to_s)
end

decl.members[4].tap do |member|
assert_instance_of RBS::AST::Members::MethodDefinition, member
assert_equal :method, member.kind
assert_equal :deconstruct_keys, member.name
assert_equal ["(nil) -> {a: Integer} | (Array(Symbol)) -> Hash[untyped]"], member.method_types.map(&:to_s)
assert_equal 1, member.overloads.size
assert_equal "() -> ::Array[::Symbol]", member.overloads[0].method_type.to_s
end

decl.members[5].tap do |member|
assert_instance_of RBS::AST::Members::MethodDefinition, member
assert_equal :method, member.kind
assert_equal :with, member.name
assert_equal ["(?a: Integer) -> Foo"], member.method_types.map(&:to_s)
end
# members[3].tap do |member|
# assert_instance_of RBS::AST::Members::MethodDefinition, member
# assert_equal :method, member.kind
# assert_equal :deconstruct, member.name
# assert_equal 1, member.overloads.size
# assert_equal "() -> [Integer]", member.overloads[0].method_type.to_s
# end

# members[4].tap do |member|
# assert_instance_of RBS::AST::Members::MethodDefinition, member
# assert_equal :method, member.kind
# assert_equal :deconstruct_keys, member.name
# assert_equal 2, member.overloads.size
# assert_equal "(nil) -> {a: Integer}", member.overloads[0].method_type.to_s
# assert_equal"(Array(Symbol)) -> Hash[untyped]", member.overloads[1].method_type.to_s
# end

# members[5].tap do |member|
# assert_instance_of RBS::AST::Members::MethodDefinition, member
# assert_equal :method, member.kind
# assert_equal :with, member.name
# assert_equal 1, member.overloads.size
# assert_equal "(?a: Integer) -> Foo", member.overloads[0].method_type.to_s
# end
end

decls[1].tap do |decl|
# as funcall: Foo[1]
assert_instance_of RBS::Types::UntypedFunction, decl.block.type
assert_equal TypeName("Foo"), decl.name
assert_equal ["(::Integer a) -> Foo |(a: ::Integer) -> Foo"], member.method_types.map(&:to_s)
end
# decls[1].tap do |decl|
# # as funcall: Foo[1]
# assert_instance_of RBS::Types::UntypedFunction, decl.block.type
# assert_equal TypeName("Foo"), decl.name
# assert_equal ["(::Integer a) -> Foo |(a: ::Integer) -> Foo"], member.method_types.map(&:to_s)
# end
end
end

Expand Down

0 comments on commit bd70d45

Please sign in to comment.