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

Make calls recognize associations #536

Conversation

JosephWood2001
Copy link
Contributor

refactors the call chain correlation to make it much simpler, and also adds some functionality to allow it to parse call chains that may have been modified by an association (association modified call chains might have a function call in the middle of the call chain)

recognizes associations and applies them to call chains. Additionally, the rhs of associations are called when the association is made, and these are now also added as call chains.

Copy link
Member

@ZedThree ZedThree left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all your work on this @JosephWood2001, this is a really useful addition. I have a few comments, but they're all pretty minor

"""
adds a batch of associations to the associations dictionary
"""
self._batches.append({})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest flipping this round a little bit, doing something like:

        current_batch = {}
        # Fill current batch
        ...
        self._batches.append(current_batch)

This just saves the mental overhead of parsing self._batches[-1] all the time

self._batches.append({})
for item in associations:
# parse the association
item = item.split("=>")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be clearer as:

Suggested change
item = item.split("=>")
new, old = item.split("=>")

Combined with the above, we remove lots of the indexing in favour of clearer names

Comment on lines 115 to 116
if k not in self._dict:
self._dict[k] = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change self._dict into a collections.defaultdict(list) and drop these two lines

"""

def __init__(self):
self._dict: Dict[str, List[List[str]]] = {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't quite work out why the nested list -- please could you clarify?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the inner list represents a call chain, the outer list represents all the call chains mapped to this items key, in order that they've been mapped. Multiple call chains can be mapped to one key. i.e.:
ASSOCIATION(tmp => a%b)
ASSOCIATION(tmp => c%d)
call tmp
END ASSOCIATION
call tmp
END ASSOCIATION
in this example, the first call tmp should map to the most recent association, call c%d. After we leave this association, we should still remember the previous association, and call tmp should map to call a%b

It's basically another way of representing the list of batches (List[Dict[str, List[str]]] => Dict[str, List[List[str]]])
and by having both it improves the time complexity. Looking back at this, this is silly, as the count of Batches is obviously negligible. I've rewritten it without the unnecessary _dict.

Comment on lines 981 to 984
# Not raising an error here as too much possibility that something
# has been misidentified as a function call
if not hasattr(self, "calls"):
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please could you restore this bit? I know it's not technically needed, but it silences a couple of mypy errors

Comment on lines 1426 to 1428
if context is None:
# failed to find context
return None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should call self.print_error with a warning message as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure about this. Not finding the context just means that any of the contexts leading up to the call aren't registered in Ford. This could simply be because the context is from an external module, and could be quite common, and expected in some codebases.

This is done by looking at the first label in the call chain and matching it to
a variable or function in the current scope. Then, traverse to the context of the
variable or function return and repeat until the call chain is exhausted.
a variable, function ext. in the current scope. Then, switch to the context of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is ext. a typo?

if call_type is None:
return None
context = self
for i, call in enumerate(call_chain):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this whole bit is much clearer!


# Register the associations
assoc_str = ford.utils.strip_paren(
self.ASSOCIATE_RE.match(line).group(2), 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could make this even better by using match := self.ASSOCIATE_RE.match(line) in the elif so we can reuse match, and then also naming the group in ASSOCIATE_RE so we can do match[<group name>] here

Comment on lines 1409 to 1410
if i == len(call_chain) - 1:
return item
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider rewriting the loop to just be over call_chain[:-1]?

@JosephWood2001
Copy link
Contributor Author

👍 applied suggestions, except for adding a warning to contexts that can't be found.

Copy link
Member

@ZedThree ZedThree left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing, thanks @JosephWood2001 !

@ZedThree ZedThree merged commit 06c9bfc into Fortran-FOSS-Programmers:master Jul 3, 2023
5 checks passed
@ZedThree ZedThree added this to the v7.0.0 milestone Aug 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants