Skip to content

Commit

Permalink
Added ReturnNil annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
Hadeweka committed Apr 26, 2021
1 parent b9caac0 commit ab02b73
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 62 deletions.
10 changes: 10 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## Releases

### Version 0.9.1

#### Usability

* Allow for a wrapped function to return nil by default

#### Bugfixes

* Fixed broken documentation

### Version 0.9.0

#### Features
Expand Down
11 changes: 0 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,6 @@ The term 'anyoli' means 'green' in the Maasai language, thus naming 'anyolite'.

## Upcoming releases

### Version 0.9.1

#### Usability

* [ ] Allow for a wrapped function to return nil by default

#### Bugfixes

* [ ] Fixed broken documentation
* [ ] UInt does now work as argument type

### Version 0.10.0

This version is planned to be the last feature release before 1.0.0.
Expand Down
5 changes: 4 additions & 1 deletion examples/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,7 @@
same_as_a = TestModule::Test.new(x: a.x)

puts "Are a and b equal? #{a == b}"
puts "Are a and same_as_a equal? #{a == same_as_a}"
puts "Are a and same_as_a equal? #{a == same_as_a}"

puts a.uint_test(arg: 123)
puts a.noreturn_test.class
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: anyolite
version: 0.9.0
version: 0.9.1

authors:
- Hadeweka
Expand Down
39 changes: 27 additions & 12 deletions src/Main.cr
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ module Anyolite
#
# The value *operator* will append the specified `String`
# to the final name and *context* can give the function a `Path` for resolving types correctly.
macro wrap_module_function(rb_interpreter, under_module, name, proc, proc_args = nil, operator = "", context = nil)
Anyolite::Macro.wrap_module_function_with_args({{rb_interpreter}}, {{under_module}}, {{name}}, {{proc}}, {{proc_args}}, operator: {{operator}}, context: {{context}})
# The value *return_nil* will override any returned value with `nil`.
macro wrap_module_function(rb_interpreter, under_module, name, proc, proc_args = nil, operator = "", context = nil, return_nil = false)
Anyolite::Macro.wrap_module_function_with_args({{rb_interpreter}}, {{under_module}}, {{name}}, {{proc}}, {{proc_args}}, operator: {{operator}}, context: {{context}}, return_nil: {{return_nil}})
end

# Wraps a module function into mruby, using keyword arguments.
Expand All @@ -122,8 +123,9 @@ module Anyolite
#
# The value *operator* will append the specified `String`
# to the final name and *context* can give the function a `Path` for resolving types correctly.
macro wrap_module_function_with_keywords(rb_interpreter, under_module, name, proc, keyword_args, regular_args = nil, operator = "", context = nil)
Anyolite::Macro.wrap_module_function_with_keyword_args({{rb_interpreter}}, {{under_module}}, {{name}}, {{proc}}, {{keyword_args}}, {{regular_args}}, operator: {{operator}}, context: {{context}})
# The value *return_nil* will override any returned value with `nil`.
macro wrap_module_function_with_keywords(rb_interpreter, under_module, name, proc, keyword_args, regular_args = nil, operator = "", context = nil, return_nil = false)
Anyolite::Macro.wrap_module_function_with_keyword_args({{rb_interpreter}}, {{under_module}}, {{name}}, {{proc}}, {{keyword_args}}, {{regular_args}}, operator: {{operator}}, context: {{context}}, return_nil: {{return_nil}})
end

# Wraps a class method into mruby.
Expand All @@ -135,8 +137,9 @@ module Anyolite
#
# The value *operator* will append the specified `String`
# to the final name and *context* can give the function a `Path` for resolving types correctly.
macro wrap_class_method(rb_interpreter, crystal_class, name, proc, proc_args = nil, operator = "", context = nil)
Anyolite::Macro.wrap_class_method_with_args({{rb_interpreter}}, {{crystal_class}}, {{name}}, {{proc}}, {{proc_args}}, operator: {{operator}}, context: {{context}})
# The value *return_nil* will override any returned value with `nil`.
macro wrap_class_method(rb_interpreter, crystal_class, name, proc, proc_args = nil, operator = "", context = nil, return_nil = false)
Anyolite::Macro.wrap_class_method_with_args({{rb_interpreter}}, {{crystal_class}}, {{name}}, {{proc}}, {{proc_args}}, operator: {{operator}}, context: {{context}}, return_nil: {{return_nil}})
end

# Wraps a class method into mruby, using keyword arguments.
Expand All @@ -148,8 +151,9 @@ module Anyolite
#
# The value *operator* will append the specified `String`
# to the final name and *context* can give the function a `Path` for resolving types correctly.
macro wrap_class_method_with_keywords(rb_interpreter, crystal_class, name, proc, keyword_args, regular_args = nil, operator = "", context = nil)
Anyolite::Macro.wrap_class_method_with_keyword_args({{rb_interpreter}}, {{crystal_class}}, {{name}}, {{proc}}, {{keyword_args}}, {{regular_args}}, operator: {{operator}}, context: {{context}})
# The value *return_nil* will override any returned value with `nil`.
macro wrap_class_method_with_keywords(rb_interpreter, crystal_class, name, proc, keyword_args, regular_args = nil, operator = "", context = nil, return_nil = false)
Anyolite::Macro.wrap_class_method_with_keyword_args({{rb_interpreter}}, {{crystal_class}}, {{name}}, {{proc}}, {{keyword_args}}, {{regular_args}}, operator: {{operator}}, context: {{context}}, return_nil: {{return_nil}})
end

# Wraps an instance method into mruby.
Expand All @@ -161,8 +165,9 @@ module Anyolite
#
# The value *operator* will append the specified `String`
# to the final name and *context* can give the function a `Path` for resolving types correctly.
macro wrap_instance_method(rb_interpreter, crystal_class, name, proc, proc_args = nil, operator = "", context = nil)
Anyolite::Macro.wrap_instance_function_with_args({{rb_interpreter}}, {{crystal_class}}, {{name}}, {{proc}}, {{proc_args}}, operator: {{operator}}, context: {{context}})
# The value *return_nil* will override any returned value with `nil`.
macro wrap_instance_method(rb_interpreter, crystal_class, name, proc, proc_args = nil, operator = "", context = nil, return_nil = false)
Anyolite::Macro.wrap_instance_function_with_args({{rb_interpreter}}, {{crystal_class}}, {{name}}, {{proc}}, {{proc_args}}, operator: {{operator}}, context: {{context}}, return_nil: {{return_nil}})
end

# Wraps an instance method into mruby, using keyword arguments.
Expand All @@ -174,8 +179,9 @@ module Anyolite
#
# The value *operator* will append the specified `String`
# to the final name and *context* can give the function a `Path` for resolving types correctly.
macro wrap_instance_method_with_keywords(rb_interpreter, crystal_class, name, proc, keyword_args, regular_args = nil, operator = "", context = nil)
Anyolite::Macro.wrap_instance_function_with_keyword_args({{rb_interpreter}}, {{crystal_class}}, {{name}}, {{proc}}, {{keyword_args}}, {{regular_args}}, operator: {{operator}}, context: {{context}})
# The value *return_nil* will override any returned value with `nil`.
macro wrap_instance_method_with_keywords(rb_interpreter, crystal_class, name, proc, keyword_args, regular_args = nil, operator = "", context = nil, return_nil = false)
Anyolite::Macro.wrap_instance_function_with_keyword_args({{rb_interpreter}}, {{crystal_class}}, {{name}}, {{proc}}, {{keyword_args}}, {{regular_args}}, operator: {{operator}}, context: {{context}}, return_nil: {{return_nil}})
end

# Wraps a setter into mruby.
Expand Down Expand Up @@ -300,6 +306,15 @@ module Anyolite
# arguments (`-1` for all arguments).
annotation WrapWithoutKeywordsClassMethod; end

# Lets the function always return `nil`.
annotation ReturnNil; end

# Lets the instance method given as the first argument always return `nil`.
annotation ReturnNilInstanceMethod; end

# Lets the class method given as the first argument always return `nil`.
annotation ReturnNilClassMethod; end

# Specifies the generic type names for the following class as its argument,
# in form of an `Array` of their names.
annotation SpecifyGenericTypes; end
Expand Down
2 changes: 2 additions & 0 deletions src/implementations/Rb3Impl.cr
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# Ruby is currently not supported, so this will do nothing

raise("Ruby is currently not supported for implementation.")
34 changes: 26 additions & 8 deletions src/macros/FunctionCalls.cr
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
module Anyolite
module Macro
macro call_and_return(rb, proc, regular_args, converted_args, operator = "")
macro call_and_return(rb, proc, regular_args, converted_args, operator = "", return_nil = false)
{% if proc.stringify == "Anyolite::Empty" %}
return_value = {{operator.id}}(*{{converted_args}})
{% else %}
return_value = {{proc}}{{operator.id}}(*{{converted_args}})
{% end %}
Anyolite::RbCast.return_value({{rb}}, return_value)

{% if return_nil %}
Anyolite::RbCast.return_nil
{% else %}
Anyolite::RbCast.return_value({{rb}}, return_value)
{% end %}
end

macro call_and_return_keyword_method(rb, proc, converted_regular_args, keyword_args, kw_args, operator = "",
empty_regular = false, context = nil, type_vars = nil, type_var_names = nil)
empty_regular = false, context = nil, type_vars = nil, type_var_names = nil, return_nil = false)

{% if proc.stringify == "Anyolite::Empty" %}
return_value = {{operator.id}}(
Expand All @@ -35,10 +40,14 @@ module Anyolite
{% end %}
)

Anyolite::RbCast.return_value({{rb}}, return_value)
{% if return_nil %}
Anyolite::RbCast.return_nil
{% else %}
Anyolite::RbCast.return_value({{rb}}, return_value)
{% end %}
end

macro call_and_return_instance_method(rb, proc, converted_obj, converted_args, operator = "")
macro call_and_return_instance_method(rb, proc, converted_obj, converted_args, operator = "", return_nil = false)
if {{converted_obj}}.is_a?(Anyolite::StructWrapper)
working_content = {{converted_obj}}.content

Expand All @@ -56,11 +65,16 @@ module Anyolite
return_value = {{converted_obj}}.{{proc}}{{operator.id}}(*{{converted_args}})
{% end %}
end
Anyolite::RbCast.return_value({{rb}}, return_value)

{% if return_nil %}
Anyolite::RbCast.return_nil
{% else %}
Anyolite::RbCast.return_value({{rb}}, return_value)
{% end %}
end

macro call_and_return_keyword_instance_method(rb, proc, converted_obj, converted_regular_args, keyword_args, kw_args, operator = "",
empty_regular = false, context = nil, type_vars = nil, type_var_names = nil)
empty_regular = false, context = nil, type_vars = nil, type_var_names = nil, return_nil = false)

if {{converted_obj}}.is_a?(Anyolite::StructWrapper)
working_content = {{converted_obj}}.content
Expand Down Expand Up @@ -116,7 +130,11 @@ module Anyolite

end

Anyolite::RbCast.return_value({{rb}}, return_value)
{% if return_nil %}
Anyolite::RbCast.return_nil
{% else %}
Anyolite::RbCast.return_value({{rb}}, return_value)
{% end %}
end
end
end
26 changes: 21 additions & 5 deletions src/macros/WrapAll.cr
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ module Anyolite
{% all_annotations_without_keywords_im = crystal_class.resolve.annotations(Anyolite::WrapWithoutKeywordsInstanceMethod) %}
{% annotation_without_keyword_im = all_annotations_without_keywords_im.find { |element| element[0].id.stringify == method.name.stringify } %}

{% all_annotations_return_nil_im = crystal_class.resolve.annotations(Anyolite::ReturnNilInstanceMethod) %}
{% annotation_return_nil_im = all_annotations_return_nil_im.find { |element| element[0].id.stringify == method.name.stringify } %}

{% if method.annotation(Anyolite::Rename) %}
{% ruby_name = method.annotation(Anyolite::Rename)[0].id %}
{% elsif annotation_rename_im && method.name.stringify == annotation_rename_im[0].stringify %}
Expand All @@ -57,6 +60,11 @@ module Anyolite
{% without_keywords = annotation_without_keyword_im[1] ? annotation_without_keyword_im[1] : -1 %}
{% end %}

{% return_nil = false %}
{% if method.annotation(Anyolite::ReturnNil) || (annotation_return_nil_im) %}
{% return_nil = true %}
{% end %}

{% puts "> Processing instance method #{crystal_class}::#{method.name} to #{ruby_name}\n--> Args: #{method.args}" if verbose %}

# Ignore private and protected methods (can't be called from outside, they'd need to be wrapped for this to work)
Expand All @@ -79,15 +87,15 @@ module Anyolite
{% elsif method.name[-1..-1] =~ /\W/ %}
{% operator = ruby_name %}

Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", operator: "{{operator}}", without_keywords: -1, context: {{context}})
Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", operator: "{{operator}}", without_keywords: -1, context: {{context}}, return_nil: {{return_nil}})
{% how_many_times_wrapped[ruby_name.stringify] = how_many_times_wrapped[ruby_name.stringify] ? how_many_times_wrapped[ruby_name.stringify] + 1 : 1 %}
# Handle constructors
{% elsif method.name == "initialize" && use_enum_constructor == false %}
Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", is_constructor: true, without_keywords: {{without_keywords}}, added_keyword_args: {{added_keyword_args}}, context: {{context}})
Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", is_constructor: true, without_keywords: {{without_keywords}}, added_keyword_args: {{added_keyword_args}}, context: {{context}}, return_nil: {{return_nil}})
{% how_many_times_wrapped[ruby_name.stringify] = how_many_times_wrapped[ruby_name.stringify] ? how_many_times_wrapped[ruby_name.stringify] + 1 : 1 %}
# Handle other instance methods
{% else %}
Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", without_keywords: {{without_keywords}}, added_keyword_args: {{added_keyword_args}}, context: {{context}})
Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", without_keywords: {{without_keywords}}, added_keyword_args: {{added_keyword_args}}, context: {{context}}, return_nil: {{return_nil}})
{% how_many_times_wrapped[ruby_name.stringify] = how_many_times_wrapped[ruby_name.stringify] ? how_many_times_wrapped[ruby_name.stringify] + 1 : 1 %}
{% end %}

Expand Down Expand Up @@ -138,6 +146,9 @@ module Anyolite
{% all_annotations_without_keywords_im = crystal_class.resolve.annotations(Anyolite::WrapWithoutKeywordsClassMethod) %}
{% annotation_without_keyword_im = all_annotations_without_keywords_im.find { |element| element[0].id.stringify == method.name.stringify } %}

{% all_annotations_return_nil_im = crystal_class.resolve.annotations(Anyolite::ReturnNilClassMethod) %}
{% annotation_return_nil_im = all_annotations_return_nil_im.find { |element| element[0].id.stringify == method.name.stringify } %}

{% if method.annotation(Anyolite::Rename) %}
{% ruby_name = method.annotation(Anyolite::Rename)[0].id %}
{% elsif annotation_rename_im && method.name.stringify == annotation_rename_im[0].stringify %}
Expand All @@ -164,6 +175,11 @@ module Anyolite
{% without_keywords = annotation_without_keyword_im[1] ? annotation_without_keyword_im[1] : -1 %}
{% end %}

{% return_nil = false %}
{% if method.annotation(Anyolite::ReturnNil) || (annotation_return_nil_im) %}
{% return_nil = true %}
{% end %}

{% puts "> Processing class method #{crystal_class}::#{method.name} to #{ruby_name}\n--> Args: #{method.args}" if verbose %}

# Ignore private and protected methods (can't be called from outside, they'd need to be wrapped for this to work)
Expand All @@ -184,11 +200,11 @@ module Anyolite
{% elsif method.name[-1..-1] =~ /\W/ %}
{% operator = ruby_name %}

Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", operator: "{{operator}}", is_class_method: true, without_keywords: -1, context: {{context}})
Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", operator: "{{operator}}", is_class_method: true, without_keywords: -1, context: {{context}}, return_nil: {{return_nil}})
{% how_many_times_wrapped[ruby_name.stringify] = how_many_times_wrapped[ruby_name.stringify] ? how_many_times_wrapped[ruby_name.stringify] + 1 : 1 %}
# Handle other class methods
{% else %}
Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", is_class_method: true, without_keywords: {{without_keywords}}, added_keyword_args: {{added_keyword_args}}, context: {{context}})
Anyolite::Macro.wrap_method_index({{rb_interpreter}}, {{crystal_class}}, {{index}}, "{{ruby_name}}", is_class_method: true, without_keywords: {{without_keywords}}, added_keyword_args: {{added_keyword_args}}, context: {{context}}, return_nil: {{return_nil}})
{% how_many_times_wrapped[ruby_name.stringify] = how_many_times_wrapped[ruby_name.stringify] ? how_many_times_wrapped[ruby_name.stringify] + 1 : 1 %}
{% end %}

Expand Down
Loading

0 comments on commit ab02b73

Please sign in to comment.