Q: how to find all the renamed attributes of a (AST processed) class in the gec compiler ?
Hi,
I want to find all the renamed attributes of a (AST processed) class in the gec compiler:
I have checked et_class.e
and found this navigation:
ET_CLASS.queries: ET_QUERY_LIST
ET_QUERY.is_attribute
but where to find if the attribute is a renamed one?
Thanks.
This needs to be double-checked in the code, but if I recall correctly you have to look at first_precursor and other_precursors. If they are not Void, then see whether their names are the same as in the current class or not.
Thanks. I also found this navigation:
pwd: /gobo/tool/gec
../../library/tools/src/eiffel/compilation/et_feature_flattener.e flatten_inherited_feature (a_feature: ET_INHERITED_FEATURE)
../../library/tools/src/eiffel/ast/feature/et_inherited_feature.e ET_INHERITED_FEATURE flattened_parent: ET_PARENT_FEATURE -- Parent feature from which `flattened_feature' is resulting
../../library/tools/src/eiffel/ast/feature/et_parent_feature.e precursor_feature: ET_FEATURE -- Feature inherited from `parent'
../../library/tools/src/eiffel/ast/feature/et_feature.e is_attribute: BOOLEAN -- Is feature an attribute?
Do you think this is good enough to scan all the renamed attribute (I don't care about methods)?
I think it would work, except that ET_INHERITED_FEATURE objects are intermediate objects only used in the feature flattener. They are not kept in the AST objects.
So with the former solution, you can call the code of gec up to Degree 4 included, and then write your own code (without having to modify any line of code in the Gobo library) to navigate through the resulting AST (ET_CLASS.queries.item(...).first_precursor).
With the latter solution, you would probably have to modify the feature flattener class to intercept the ET_INHERITED_FEATURE objects on the fly when they are processed. Because they are thrown away after being processed.
Which solution is easiest to implement in your estimate?
(You know: "There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.")
I want to take the simple way 🙂
Simplest way is the former solution: no need to modify Gobo's code, so no risk to break it. Just call the Gobo code from your code to generate the AST, and then traverse the AST (ET_CLASS, ET_FEATURE, precursors, ...) still from your code.
Thanks.
I will use tool/gedoc/src/gedoc_implicit_converts_format.e as a template for quick start.
Hi @ebezault
I did a quick experiment using tool/gedoc/src/gedoc_implicit_converts_format.e as a template:
https://github.com/joortcom/gobo/blob/eiffel_rename/tool/gedoc/src/gedoc_field_rename_format.e#L119-L176
only two new methods:
process_et_feature
process_implicit_converts
Since gec cannot build this project (ref #71), I added a Makefile to build using ISE's ec:
https://github.com/joortcom/gobo/blob/eiffel_rename/tool/gedoc/Makefile
$ cd /gobo/tool/gedoc
$ make ise # build gedoc
$ cd test/rename/
$ make demo # try the new build
../../gedoc --format=field_rename app.ecf
Degree 6: 0/0/0 0:0:0.149
Degree 5: 0/0/0 0:0:7.063
Degree 4: 0/0/0 0:0:2.403
[renamed field] READABLE_INDEXABLE.upper => STRING_8.count
[renamed field] READABLE_INDEXABLE.upper => READABLE_STRING_8.count
[renamed field] READABLE_INDEXABLE.upper => STRING_32.count
[renamed field] TWO_WAY_LIST.first_element => TWO_WAY_TREE.first_child
[renamed field] TWO_WAY_LIST.last_element => TWO_WAY_TREE.last_child
[renamed field] LINKED_LIST.after => TWO_WAY_TREE.child_after
[renamed field] LINKED_LIST.first_element => LINKED_TREE.first_child
[renamed field] LINKED_LIST.before => TWO_WAY_TREE.child_before
[renamed field] LINKED_LIST.count => TWO_WAY_TREE.arity
[renamed field] LINKABLE.right => TWO_WAY_TREE.right_sibling
[renamed field] LINKED_LIST.after => LINKED_TREE.child_after
[renamed field] BI_LINKABLE.left => TWO_WAY_TREE.left_sibling
[renamed field] LINKED_LIST.active => TWO_WAY_TREE.child
[renamed field] LINKED_LIST.before => LINKED_TREE.child_before
[renamed field] LINKED_LIST.count => LINKED_TREE.arity
[renamed field] TWO_WAY_LIST_ITERATION_CURSOR.active => TWO_WAY_TREE_ITERATION_CURSOR.node
[renamed field] LINKABLE.right => LINKED_TREE.right_sibling
[renamed field] LINKED_LIST_ITERATION_CURSOR.active => LINKED_TREE_ITERATION_CURSOR.node
[renamed field] GENERAL_SPECIAL_ITERATION_CURSOR.area => SPECIAL_ITERATION_CURSOR.target
[renamed field] LINKED_LIST.active => LINKED_TREE.child
[renamed field] INDEXABLE_ITERATION_CURSOR.target_index => HASH_TABLE_ITERATION_CURSOR.iteration_position
[renamed field] READABLE_INDEXABLE.upper => DIRECTORY_NAME.count
[renamed field] IO_MEDIUM.handle_available => PLAIN_TEXT_FILE.descriptor_available
[renamed field] IO_MEDIUM.handle_available => RAW_FILE.descriptor_available
[renamed field] READABLE_INDEXABLE.upper => FILE_NAME.count
[renamed field] IO_MEDIUM.handle_available => FILE.descriptor_available
[renamed field] IO_MEDIUM.handle_available => CONSOLE.descriptor_available
[renamed field] READABLE_INDEXABLE.upper => PATH_NAME.count
[renamed field] READABLE_INDEXABLE.upper => READABLE_STRING_32.count
[renamed field] HASH_TABLE.count => SED_OBJECTS_TABLE.capacity
Total Time: 0/0/0 0:0:10.252
However in the test file research_assistant.e has two fields addr renames, but why it's not reported in the above output?
BTW:
$ cd test/rename/
$ make gobo # the test app program can be compiled by gec, and executed fine.
So what's going wrong? why some of the class / queries not visited?
Can you help me to make this output research_assistant.e's renamed fields?
Thanks.
In particular: looks like it does not scan from the <root class="APP" feature="make"/> class?
https://github.com/joortcom/gobo/blob/eiffel_rename/tool/gedoc/test/rename/app.ecf#L4
Before looking at the rename issue, I have a couple of remarks:
- See my comment in the other thread about
gecand CAT-calls. You should be able to adapt your code so that it does not report a CAT-call. - In your Makefile, you can replace:
ec -config src/system.ecf
cd EIFGENs/gedoc/$(DIR)/ && finish_freezing
with:
ec -c_compile -config src/system.ecf
For class RESEARCH_ASSISTANT, assuming that this is this code:
class RESEARCH_ASSISTANT
inherit
STUDENT rename addr as student_addr end -- field student_addr inherit the dorm semantics
FACULTY rename addr as faculty_addr end -- field faculty_addr inherit the lab semantics
-- then select, NOTE: not needed by SmartEiffel, but needed by GOBO and ISE compiler
PERSON select addr end
I can see that there are replications. It means that from 1 attribute addr, you created 2 new attributes (student_addr and faculty_addr) to end up with 3 attributes in class RESEARCH_ASSISTANT. The attributes student_addr and faculty_addr have no precursors (at least not in the internals of the compiler) because the selected version is the one inherited from PERSON. It's as if two new attributes had been created in class RESEARCH_ASSISTANT. So using first_precursor and other_precursors will not help in this case because it will not catch renaming occurring on a replicated attribute.
Another solution could be to traverse ET_CLASS.parent_clauses, go down to the rename clauses (ET_PARENT.renames), look at the new name (ET_RENAME.new_name.feature_name) and see whether it is an attribute in the current class (ET_CLASS.named_query (...).is_attribute).
This should compile with no CAT-call warnings with gec :-)
with:
ec -c_compile -config src/system.ecf
I tried this, the command runs fine without any error, but the executable EIFGENs/gedoc/W_code/gedoc is not updated.
Another solution could be to traverse
ET_CLASS.parent_clauses, go down to the rename clauses (ET_PARENT.renames), look at the new name (ET_RENAME.new_name.feature_name) and see whether it is an attribute in the current class (ET_CLASS.named_query (...).is_attribute).
Thanks, the navigation from ET_CLASS.parent_clauses worked:
[renamed field] STUDENT.addr => RESEARCH_ASSISTANT.student_addr
[renamed field] FACULTY.addr => RESEARCH_ASSISTANT.faculty_addr
I've got the renamed fields path. Now, how do I get inherited (instead of self-declared) fields path? i.e.
PERSON.addr => STUDENT.addr (since STUDENT does not declare .addr in itself, but inherited from PERSON)
PERSON.addr => FACULTY.addr (same as above)
with:
ec -c_compile -config src/system.ecfI tried this, the command runs fine without any error, but the executable
EIFGENs/gedoc/W_code/gedocis not updated.
Forcing the C compilation by calling finish_freezing will regenerate the same executable. What is updated after an incremental compilation is the .melted file, not the C files. If you want the C files to be updated, add the command-line option -freeze.
I got the renamed fields path. Now, how do I get inherited (instead of self-declared) fields path? i.e.
PERSON.addr => STUDENT.addr (since STUDENT does not declare .addr in itself, but inherited from PERSON) PERSON.addr => FACULTY.addr (same as above)
OK, so you are not just interested in renamed attributes anymore, but in all inherited attributes! Perhaps you should combine the two implementations (traversing the parent clauses for the renamings, and the first and other precursors to detect those which are not renamed). If it has a precursor, it means that it is inherited. The reverse is not true as we could see in case of replication.
Otherwise, in ET_CLASS.queries, the queries between 1 and `declared_count' are declared in the current class, and above they are inherited. But be careful, declared in the current class does not mean that they have been introduced in this class. It can also mean that they are inherited and redefined.
I'm sorry that the solution is not as easy as it could be. This code has been written for the specific needs of gec, so it might be missing some metadata which were not needed by gec. But hopefully it should be possible to reconstruct what you need by combining several approaches, as suggested above.
I'm still interested in renamed attributes, but need to dig deeper: basically I want to detect renamed attributes that coming from the same origin (e.g. in the case of diamond problem).
So:
[renamed field] STUDENT.addr => RESEARCH_ASSISTANT.student_addr
[renamed field] FACULTY.addr => RESEARCH_ASSISTANT.faculty_addr
I need to dig deeper:
PERSON.addr => STUDENT.addr (since STUDENT does not declare .addr in itself, but inherited from PERSON)
PERSON.addr => FACULTY.addr (same as above)
and find out RESEARCH_ASSISTANT.student_addr and RESEARCH_ASSISTANT.faculty_addr are both originated from the same PERSON.addr. This is the goal I want to detect.
Sounds like I need to take this 1st approach:
Perhaps you should combine the two implementations (traversing the parent clauses for the renamings, and the first and other precursors to detect those which are not renamed). If it has a precursor, it means that it is inherited. The reverse is not true as we could see in case of replication.
I will give it a try.
I just remembered that there is another data at your disposal (which you can use in combination with all the above):
ET_FEATURE.implementation_classtells you where the current feature was written,ET_FEATURE.implementation_featureis the object of this feature inET_FEATURE.implementation_class(which might be different from the object of this feature in the current class, in case of renaming for example, but it can also be when joining or when changing the export status among other things).
ET_FEATURE.implementation_classtells you where the current feature was written,
what does "where the current feature was written" esp "written" mean? first time introduced? or last time (final)?
ET_FEATURE.implementation_classtells you where the current feature was written,what does "where the current feature was written" esp "written" mean? first time introduced? or last time (final)?
Last time. "Written" means in which class file can I find its text. So if I have:
class A
feature
f
do
...
end
end
class B
inherit
A
redefine
f
end
feature
f
do
...
end
end
class C
inherit
B
end
then feature C.f is written in class B.
And a more complicated example:
class D
feature
f
do
...
end
end
class E
inherit
D
undefine
f
end
C
end
then E.f is written in class B (because the version from D is undefined).
Tried this, but it's not working for PERSON.addr:
If it has a precursor, it means that it is inherited.
My code is here: https://github.com/joortcom/gobo/blob/eiffel_rename/tool/gedoc/src/gedoc_field_rename_format.e#L125-L136
output is here: https://github.com/joortcom/gobo/blob/eiffel_rename/tool/gedoc/test/rename/inherited_fields.path
it worked for things like this one:
https://github.com/joortcom/gobo/blob/a3af3982a213fdbf3886d1e4c52a505f2697be9b/tool/gedoc/test/rename/inherited_fields.path#L43
but not for PERSON.addr => FACULTY.addr.
Will continue try the other method ET_FEATURE.implementation_class.
If it has a precursor, it means that it is inherited. The reverse is not true
So we are unfortunately in the case where "the reverse is not true" (no precursor does not mean that it is not inherited).
Looking at your code, I still recommend using:
if l_first_precursor.name.same_feature_name(query.name) then
instead of:
if l_lower_name.is_equal(query.lower_name) then
It's CAT-call free.
I just remembered that there is another data at your disposal (which you can use in combination with all the above):
ET_FEATURE.implementation_classtells you where the current feature was written,ET_FEATURE.implementation_featureis the object of this feature inET_FEATURE.implementation_class(which might be different from the object of this feature in the current class, in case of renaming for example, but it can also be when joining or when changing the export status among other things).
This approach worked:
??? field: PERSON.addr => FACULTY.addr
[renamed field] STUDENT.addr => RESEARCH_ASSISTANT.student_addr
??? field: PERSON.addr => STUDENT.addr
[renamed field] FACULTY.addr => RESEARCH_ASSISTANT.faculty_addr
??? field: PERSON.addr => RESEARCH_ASSISTANT.student_addr
??? field: PERSON.addr => RESEARCH_ASSISTANT.faculty_addr
??? field: PERSON.addr => RESEARCH_ASSISTANT.addr
But now, I want to filter out non-direct super-sub-class relationship, i.e filter out:
??? field: PERSON.addr => RESEARCH_ASSISTANT.student_addr
??? field: PERSON.addr => RESEARCH_ASSISTANT.faculty_addr
So how to get a ET_CLASS's direct super-classes / sub-classes?
So how to get a ET_CLASS's direct super-classes / sub-classes?
You get the direct super-classes with ET_CLASS.parent_clauses.
For the direct sub-classes, you will have to build them yourself. You can have a look at ET_CLASS.descendants which builds all sub-classes (direct and indirect).
Is there a way to get rid of this: is_equal for class name:
https://github.com/joortcom/gobo/blob/eiffel_rename/tool/gedoc/src/gedoc_field_rename_format.e#L124
if not query.implementation_class.upper_name.is_equal(a_class.upper_name) then
if not query.implementation_class.name.same_class_name (a_class.name) then
You can also write:
if query.implementation_class /= a_class then
which is probably better because it is possible to have two classes with the same name from different libraries.
And how about compare:
a ET_CLASS and a parent.type is the same class?
And how about compare:
a
ET_CLASSand aparent.typeis the same class?
if l_parent.type.base_class = l_class then