Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add public/private per member modifier #911

Merged
merged 4 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 51 additions & 10 deletions docs/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,12 @@ _member_ ::= _ivar-member_ # Ivar definition
| _extend-member_ # Mixin (extend)
| _prepend-member_ # Mixin (prepend)
| _alias-member_ # Alias
| `public` # Public
| `private` # Private
| _visibility-member_ # Visibility member

_ivar-member_ ::= _ivar-name_ `:` _type_

_method-member_ ::= `def` _method-name_ `:` _method-types_ # Instance method
| `def self.` _method-name_ `:` _method-types_ # Singleton method
_method-member_ ::= _visibility_ `def` _method-name_ `:` _method-types_ # Instance method
| _visibility_ `def self.` _method-name_ `:` _method-types_ # Singleton method
| `def self?.` _method-name_ `:` _method-types_ # Singleton and instance method

_method-types_ ::= _method-type-parameters_ _method-type_ # Single method type
Expand All @@ -295,9 +294,11 @@ _method-types_ ::= _method-type-parameters_ _method-type_
_method-type-parameters_ ::= # Empty
| `[` _type-variable_ `,` ... `]`

_attribute-member_ ::= _attribute-type_ _method-name_ `:` _type_ # Attribute
| _attribute-type_ _method-name_ `(` _ivar-name_ `) :` _type_ # Attribute with variable name specification
| _attribute-type_ _method-name_ `() :` _type_ # Attribute without variable
_attribute-member_ ::= _visibility_ _attribute-type_ _method-name_ `:` _type_ # Attribute
| _visibility_ _attribute-type_ _method-name_ `(` _ivar-name_ `) :` _type_ # Attribute with variable name specification
| _visibility_ _attribute-type_ _method-name_ `() :` _type_ # Attribute without variable

_visibility_ ::= `public` | `private`

_attribute-type_ ::= `attr_reader` | `attr_writer` | `attr_accessor`

Expand All @@ -310,6 +311,8 @@ _prepend-member_ ::= `prepend` _class-name_ _type-arguments_
_alias-member_ ::= `alias` _method-name_ _method-name_
| `alias self.` _method-name_ `self.` _method-name_

_visibility-member_ ::= _visibility_

_ivar-name_ ::= /@\w+/
_method-name_ ::= ...
| /`[^`]+`/
Expand Down Expand Up @@ -353,6 +356,16 @@ def +: (Float | Integer) -> (Float | Integer)
| (Numeric) -> Numeric
```

Adding `public` and `private` modifier changes the visibility of the method.

```
private def puts: (*untyped) -> void # Defines private instance method

public def self.puts: (*untyped) -> void # Defines public singleton method

public def self?.puts: (*untyped) -> void # 🚨🚨🚨 Error: `?.` has own visibility semantics (== `module_function`) 🚨🚨🚨
```

### Attribute definition

Attribute definitions help to define methods and instance variables based on the convention of `attr_reader`, `attr_writer` and `attr_accessor` methods in Ruby.
Expand All @@ -376,6 +389,14 @@ attr_accessor people (): Array[Person]
# def people=: (Array[Person]) -> Array[Person]
```

Attribute definitions can have the `public` and `private` modifiers like method definitions:

```
private attr_accessor id: Integer

private attr_reader self.name: String
```

### Mixin (include), Mixin (extend), Mixin (prepend)

You can define mixins between class and modules.
Expand Down Expand Up @@ -404,11 +425,31 @@ def map: [X] () { (String) -> X } -> Array[X]
alias collect map # `#collect` has the same type with `map`
```

### `public`, `private`
### Visibility member

`public` and `private` allows specifying the visibility of instance methods.
Visibility member allows specifying the default visibility of instance methods and instance attributes.

These work only as _statements_, not per-method specifier.
```rbs
public

def foo: () -> void # public instance method

attr_reader name: String # public instance attribute

private

def bar: () -> void # private instance method

attr_reader email: String # private instance attribute
```

The visibility _modifiers_ overwrite the default visibility per member bases.

The visibility member requires a new line `\n` after the token.

```rbs
private alias foo bar # Syntax error
```

## Declarations

Expand Down
5 changes: 5 additions & 0 deletions ext/rbs_extension/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ unsigned int peek(lexstate *state);
* */
void skip(lexstate *state);

/**
* Skip n characters.
* */
void skipn(lexstate *state, size_t size);

/**
* Return new token with given type.
* */
Expand Down
6 changes: 6 additions & 0 deletions ext/rbs_extension/lexstate.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ void skip(lexstate *state) {
}
}

void skipn(lexstate *state, size_t size) {
for (size_t i = 0; i < size; i ++) {
skip(state);
}
}

char *peek_token(lexstate *state, token tok) {
return RSTRING_PTR(state->string) + tok.range.start.byte_pos;
}
95 changes: 85 additions & 10 deletions ext/rbs_extension/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1430,6 +1430,8 @@ InstanceSingletonKind parse_instance_singleton_kind(parserstate *state, bool all

/**
* def_member ::= {kDEF} method_name `:` <method_types>
* | {kPRIVATE2} kDEF method_name `:` <method_types>
* | {kPUBLIC2} kDEF method_name `:` <method_types>
*
* method_types ::= {} <method_type>
* | {} <`...`>
Expand All @@ -1440,30 +1442,61 @@ InstanceSingletonKind parse_instance_singleton_kind(parserstate *state, bool all
* */
VALUE parse_member_def(parserstate *state, bool instance_only, bool accept_overload, position comment_pos, VALUE annotations) {
range member_range;
range visibility_range;
range keyword_range;
range name_range;
range kind_range;
range overload_range = NULL_RANGE;

keyword_range = state->current_token.range;
member_range.start = keyword_range.start;
VALUE visibility;

comment_pos = nonnull_pos_or(comment_pos, keyword_range.start);
member_range.start = state->current_token.range.start;
comment_pos = nonnull_pos_or(comment_pos, member_range.start);
VALUE comment = get_comment(state, comment_pos.line);

switch (state->current_token.type)
{
case kPRIVATE:
visibility_range = state->current_token.range;
visibility = ID2SYM(rb_intern("private"));
member_range.start = visibility_range.start;
parser_advance(state);
break;
case kPUBLIC:
visibility_range = state->current_token.range;
visibility = ID2SYM(rb_intern("public"));
member_range.start = visibility_range.start;
parser_advance(state);
break;
default:
visibility_range = NULL_RANGE;
visibility = Qnil;
break;
}

keyword_range = state->current_token.range;

InstanceSingletonKind kind;
if (instance_only) {
kind_range = NULL_RANGE;
kind = INSTANCE_KIND;
} else {
kind = parse_instance_singleton_kind(state, true, &kind_range);
kind = parse_instance_singleton_kind(state, NIL_P(visibility), &kind_range);
}

VALUE name = parse_method_name(state, &name_range);
VALUE method_types = rb_ary_new();
VALUE overload = Qfalse;

parser_advance_assert(state, pCOLON);
if (state->next_token.type == pDOT && RB_SYM2ID(name) == rb_intern("self?")) {
raise_syntax_error(
state,
state->next_token,
"`self?` method cannot have visibility"
);
} else {
parser_advance_assert(state, pCOLON);
}

parser_push_typevar_table(state, kind != INSTANCE_KIND);

Expand Down Expand Up @@ -1533,6 +1566,7 @@ VALUE parse_member_def(parserstate *state, bool instance_only, bool accept_overl
rbs_loc_add_required_child(loc, rb_intern("name"), name_range);
rbs_loc_add_optional_child(loc, rb_intern("kind"), kind_range);
rbs_loc_add_optional_child(loc, rb_intern("overload"), overload_range);
rbs_loc_add_optional_child(loc, rb_intern("visibility"), visibility_range);

return rbs_ast_members_method_definition(
name,
Expand All @@ -1541,7 +1575,8 @@ VALUE parse_member_def(parserstate *state, bool instance_only, bool accept_overl
annotations,
location,
comment,
overload
overload,
visibility
);
}

Expand Down Expand Up @@ -1839,18 +1874,22 @@ VALUE parse_visibility_member(parserstate *state, VALUE annotations) {

/*
attribute_member ::= {attr_keyword} attr_name attr_var `:` <type>
| {visibility} attr_keyword attr_name attr_var `:` <type>
| {attr_keyword} `self` `.` attr_name attr_var `:` <type>
| {visibility} attr_keyword `self` `.` attr_name attr_var `:` <type>

attr_keyword ::= `attr_reader` | `attr_writer` | `attr_accessor`

visibility ::= `public` | `private`

attr_var ::= # empty
| `(` tAIDENT `)` # Ivar name
| `(` `)` # No variable
*/
VALUE parse_attribute_member(parserstate *state, position comment_pos, VALUE annotations) {
range member_range;
range keyword_range, name_range, colon_range;
range kind_range = NULL_RANGE, ivar_range = NULL_RANGE, ivar_name_range = NULL_RANGE;
range kind_range = NULL_RANGE, ivar_range = NULL_RANGE, ivar_name_range = NULL_RANGE, visibility_range = NULL_RANGE;

InstanceSingletonKind is_kind;
VALUE klass;
Expand All @@ -1860,12 +1899,31 @@ VALUE parse_attribute_member(parserstate *state, position comment_pos, VALUE ann
VALUE type;
VALUE comment;
VALUE location;
VALUE visibility;
rbs_loc *loc;

member_range.start = state->current_token.range.start;
comment_pos = nonnull_pos_or(comment_pos, member_range.start);
comment = get_comment(state, comment_pos.line);

switch (state->current_token.type)
{
case kPRIVATE:
visibility = ID2SYM(rb_intern("private"));
visibility_range = state->current_token.range;
parser_advance(state);
break;
case kPUBLIC:
visibility = ID2SYM(rb_intern("public"));
visibility_range = state->current_token.range;
parser_advance(state);
break;
default:
visibility = Qnil;
visibility_range = NULL_RANGE;
break;
}

keyword_range = state->current_token.range;
switch (state->current_token.type)
{
Expand Down Expand Up @@ -1924,6 +1982,7 @@ VALUE parse_attribute_member(parserstate *state, position comment_pos, VALUE ann
rbs_loc_add_optional_child(loc, rb_intern("kind"), kind_range);
rbs_loc_add_optional_child(loc, rb_intern("ivar"), ivar_range);
rbs_loc_add_optional_child(loc, rb_intern("ivar_name"), ivar_name_range);
rbs_loc_add_optional_child(loc, rb_intern("visibility"), visibility_range);

return rbs_ast_members_attribute(
klass,
Expand All @@ -1933,7 +1992,8 @@ VALUE parse_attribute_member(parserstate *state, position comment_pos, VALUE ann
kind,
annotations,
location,
comment
comment,
visibility
);
}

Expand Down Expand Up @@ -2113,7 +2173,6 @@ VALUE parse_module_members(parserstate *state) {
member = parse_alias_member(state, false, annot_pos, annotations);
break;


case tAIDENT:
case tA2IDENT:
case kSELF:
Expand All @@ -2128,7 +2187,23 @@ VALUE parse_module_members(parserstate *state) {

case kPUBLIC:
case kPRIVATE:
member = parse_visibility_member(state, annotations);
if (state->next_token.range.start.line == state->current_token.range.start.line) {
switch (state->next_token.type)
{
case kDEF:
member = parse_member_def(state, false, true, annot_pos, annotations);
break;
case kATTRREADER:
case kATTRWRITER:
case kATTRACCESSOR:
member = parse_attribute_member(state, annot_pos, annotations);
break;
default:
raise_syntax_error(state, state->next_token, "method or attribute definition is expected after visibility modifier");
}
} else {
member = parse_visibility_member(state, annotations);
}
break;

default:
Expand Down
6 changes: 4 additions & 2 deletions ext/rbs_extension/ruby_objs.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ VALUE rbs_ast_decl_module(VALUE name, VALUE type_params, VALUE self_types, VALUE
);
}

VALUE rbs_ast_members_method_definition(VALUE name, VALUE kind, VALUE types, VALUE annotations, VALUE location, VALUE comment, VALUE overload) {
VALUE rbs_ast_members_method_definition(VALUE name, VALUE kind, VALUE types, VALUE annotations, VALUE location, VALUE comment, VALUE overload, VALUE visibility) {
VALUE args = rb_hash_new();
rb_hash_aset(args, ID2SYM(rb_intern("name")), name);
rb_hash_aset(args, ID2SYM(rb_intern("kind")), kind);
Expand All @@ -409,6 +409,7 @@ VALUE rbs_ast_members_method_definition(VALUE name, VALUE kind, VALUE types, VAL
rb_hash_aset(args, ID2SYM(rb_intern("location")), location);
rb_hash_aset(args, ID2SYM(rb_intern("comment")), comment);
rb_hash_aset(args, ID2SYM(rb_intern("overload")), overload);
rb_hash_aset(args, ID2SYM(rb_intern("visibility")), visibility);

return CLASS_NEW_INSTANCE(
RBS_AST_Members_MethodDefinition,
Expand Down Expand Up @@ -446,7 +447,7 @@ VALUE rbs_ast_members_mixin(VALUE klass, VALUE name, VALUE module_args, VALUE an
);
}

VALUE rbs_ast_members_attribute(VALUE klass, VALUE name, VALUE type, VALUE ivar_name, VALUE kind, VALUE annotations, VALUE location, VALUE comment) {
VALUE rbs_ast_members_attribute(VALUE klass, VALUE name, VALUE type, VALUE ivar_name, VALUE kind, VALUE annotations, VALUE location, VALUE comment, VALUE visibility) {
VALUE args = rb_hash_new();
rb_hash_aset(args, ID2SYM(rb_intern("name")), name);
rb_hash_aset(args, ID2SYM(rb_intern("type")), type);
Expand All @@ -455,6 +456,7 @@ VALUE rbs_ast_members_attribute(VALUE klass, VALUE name, VALUE type, VALUE ivar_
rb_hash_aset(args, ID2SYM(rb_intern("annotations")), annotations);
rb_hash_aset(args, ID2SYM(rb_intern("location")), location);
rb_hash_aset(args, ID2SYM(rb_intern("comment")), comment);
rb_hash_aset(args, ID2SYM(rb_intern("visibility")), visibility);

return CLASS_NEW_INSTANCE(
klass,
Expand Down
4 changes: 2 additions & 2 deletions ext/rbs_extension/ruby_objs.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ VALUE rbs_ast_decl_interface(VALUE name, VALUE type_params, VALUE members, VALUE
VALUE rbs_ast_decl_module_self(VALUE name, VALUE args, VALUE location);
VALUE rbs_ast_decl_module(VALUE name, VALUE type_params, VALUE self_types, VALUE members, VALUE annotations, VALUE location, VALUE comment);
VALUE rbs_ast_members_alias(VALUE new_name, VALUE old_name, VALUE kind, VALUE annotations, VALUE location, VALUE comment);
VALUE rbs_ast_members_attribute(VALUE klass, VALUE name, VALUE type, VALUE ivar_name, VALUE kind, VALUE annotations, VALUE location, VALUE comment);
VALUE rbs_ast_members_method_definition(VALUE name, VALUE kind, VALUE types, VALUE annotations, VALUE location, VALUE comment, VALUE overload);
VALUE rbs_ast_members_attribute(VALUE klass, VALUE name, VALUE type, VALUE ivar_name, VALUE kind, VALUE annotations, VALUE location, VALUE comment, VALUE visibility);
VALUE rbs_ast_members_method_definition(VALUE name, VALUE kind, VALUE types, VALUE annotations, VALUE location, VALUE comment, VALUE overload, VALUE visibility);
VALUE rbs_ast_members_mixin(VALUE klass, VALUE name, VALUE args, VALUE annotations, VALUE location, VALUE comment);
VALUE rbs_ast_members_variable(VALUE klass, VALUE name, VALUE type, VALUE location, VALUE comment);
VALUE rbs_ast_members_visibility(VALUE klass, VALUE location);
Expand Down
Loading