-
Notifications
You must be signed in to change notification settings - Fork 15
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
Changes to generate anonymous types inside parent type's namespace #109
Conversation
* Adds a new template anonymous_Type.templ which has macros to generate anonymous types as child classes * Modifies class.templ to include anonymous types as child classes * Modifies setter methods in class.templ to include base types in the arguments instead of `AnonymousType`s * Modifies tests to remove reference to `AnonymousType`s * Adds new util structure `AnonymousType` which will be used to render anonymous types as chilsren and modify setter methods to include base types
* Adds a new `anonymous_type.templ` template to generate a child module for anonymous types * Modifies `struct.templ` to use anonymous_type template
@@ -0,0 +1,209 @@ | |||
{# following macro defines an anonymous type as children class for its parent type definition #} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(🗺️ PR tour) This file adds 3 new macros for generating anonymous types.
anonymous_type
: Generates a new child class for given anonymous type based on its properties (e.g.target_kind_name
,abstract_data_type
).initialize_anonymous_type
: This is helper macro to initialize anonymous types inside setter methods.define_params_for_anonymous_type
: This is helper macro to define arguments to setter methods. (This macrohelps get the base type as argument instead of anonymous type itself, check this code snippet to see how this is used)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just get rid of the anonymous types when it's a List<Something>
? (Is that going to be done in a different PR?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to get rid of creating a nested class which contains List<Something>
? I have created a separate class for anonymous type with ISL like {type: list, element: int}
because there could be more constraints added to this anonymous type which might require adding more variables/APIs to this anonymous type class with List<Something>
.
But I can add a check to see if there are no more constraints then the type
and element
then I can skip generating anonymous type for it.
@@ -0,0 +1,135 @@ | |||
{# following macro defines an anonymous type as children class for its parent type definition #} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(🗺️ PR tour) This file adds a new macros for generating anonymous types. This macro generates a new child class for given anonymous type based on its properties (e.g. target_kind_name, abstract_data_type).
{# Includes the macros for anonymous types that will be added as child classes #} | ||
{% import "anonymous_type.templ" as macros %} | ||
use {{ target_kind_name | snake }}::{{ target_kind_name }}; | ||
pub mod {{ target_kind_name | snake }} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(🗺️ PR tour) This change adds a new module with the name top level type definition. This module is where all the intermediate/anonymous types will be generated.
@@ -159,4 +173,8 @@ public final class {{ target_kind_name }} { | |||
writer.stepOut(); | |||
{% endif %} | |||
} | |||
|
|||
{% for inline_type in anonymous_types -%} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(🗺️ PR tour) This is where all the anonymous types will be generated for given top level type definition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this approach also work for nested types within other nested types? E.g.:
type::{
name: Foo
fields: {
position: {
x: {
fields: {
real_part: float,
imaginary_part: float
}
},
y: {
fields: {
real_part: float,
imaginary_part: float
}
}
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it does because the template defined for anonymous types also has statement to check for its anonymous types and generate it as a child class.
This gist has generated code for your example ISL.
Here's the statement in anonymous type template that generated nested types within other nested types: https://github.com/amazon-ion/ion-cli/pull/109/files#diff-4316040659b5b43183dc6dbee67e1f131e4942f950cd2e017b7c24b352b2e51dR197
} | ||
|
||
{% for inline_type in anonymous_types -%} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(🗺️ PR tour) This is where all the anonymous types will be generated for given top level type definition.
pub struct AnonymousType { | ||
pub(crate) target_kind_name: String, | ||
pub(crate) fields: Vec<Field>, | ||
pub(crate) abstract_data_type: AbstractDataType, | ||
pub(crate) anonymous_types: Vec<AnonymousType>, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(🗺️ PR tour) This is a utility that is used to store anonymous types and use it in the templates to render as child classes/structs for a type definition.
{{ field.name | snake }}, | ||
{% endfor %} | ||
} | ||
#[derive(Debug, Clone, Default)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(🗺️ PR tour) Most of the following changes are due to indentation of having struct inside a module. The only change to consider is here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me, though I must admit I'm not versed enough in Tera template syntax to feel like I reviewed that part thoroughly.
Do the generated unit tests exercise this new functionality?
code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java
Show resolved
Hide resolved
/** | ||
* Writes a {{ target_kind_name }} as Ion from an {@link IonWriter}. | ||
* | ||
* This method does not close the writer after writing is complete. | ||
* The caller is responsible for closing the stream associated with the writer. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like that this generates javadoc 👍
src/bin/ion/commands/beta/generate/templates/rust/anonymous_type.templ
Outdated
Show resolved
Hide resolved
code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java
Show resolved
Hide resolved
s.setC(new AnonymousType3(new ArrayList<String>())); | ||
s.setC(new ArrayList<String>()); | ||
assertEquals(true, s.getC().getValue().isEmpty(), "s.getC().isEmpty() should return `true`"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to make a similar change to the getter, or else it will just lead to user confusion. If setC(List<String> c)
accepts a list, then getC()
should return a list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't change the getters because for an inline type that is for example a struct it would require to return the struct(e.g. check this test case of nested struct hat has a field c which is also a struct ). This will be simplified once we have $codegen_name
or similar which will return class C
instead of AnonymousTypeX
.
Other option is to instead let all the getX()
methods of the nested struct be part of the parent class.
@@ -0,0 +1,209 @@ | |||
{# following macro defines an anonymous type as children class for its parent type definition #} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just get rid of the anonymous types when it's a List<Something>
? (Is that going to be done in a different PR?)
@@ -159,4 +173,8 @@ public final class {{ target_kind_name }} { | |||
writer.stepOut(); | |||
{% endif %} | |||
} | |||
|
|||
{% for inline_type in anonymous_types -%} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this approach also work for nested types within other nested types? E.g.:
type::{
name: Foo
fields: {
position: {
x: {
fields: {
real_part: float,
imaginary_part: float
}
},
y: {
fields: {
real_part: float,
imaginary_part: float
}
}
}
}
}
Yes, it does. The |
As per the conversation in this PR and offline, it would be simpler to express sequence and scalar nested types as is instead of creating another nested class/struct for it. This will be implemented in a separate PR.
Output Java:
|
Issue #103:
Description of changes:
This PR works on first portion of #103 issue which is defining a namespace corresponding to parent type for anonymous types. As described in the issue:
AnonymousType1
) will be placed inside its parent class (e.g.Foo
) in the same namespace (e.g.Foo.AnonymousType1
)AnonymousType1
) will be placed inside its parent type module(e.g.foo
).Generated code:
List of changes:
anonymous types as child classes
arguments instead of
AnonymousType
sAnonymousType
sAnonymousType
which will be used to renderanonymous types as chilsren and modify setter methods to include base
types
anonymous_type.templ
template to generate a child modulefor anonymous types
struct.templ
to use anonymous_type templateBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.