(towards #3083) Extend treesitter frontend - #3558
Conversation
|
@sergisiso At some point (after this PR is merged) can I have a look at trying to get comments into this as well? Partly to try to undesrtand how this works and partly to try to keep our current level of comment support. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3558 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 397 397
Lines 55758 56545 +787
==========================================
+ Hits 55758 56545 +787 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… to states that the pinned version does not produce
|
@LonelyCat124 This is ready for review, I keep finding things that could be better but the PR is already huge and at some point I need to cut the PR. It supports a lot more Fortran now but it is not mature enough to replace workflows yet (real_application->transformation_script->backend is still fragile). Note that I used AI assistance, I spent quite a bit of my effort getting the high level fronted design as I wanted (this is the _process_node, handler selection, using generators, utility methods, scoping with contextmanager, meaning of errors, ... I tried to document all these) but code inside the handlers and test is largely generated (just asking to follow certain patterns, e.g. 'test should start with real Fortran snippet and assert against the produced PSyIR'). Feel free to say if any large sections need to be changed. |
LonelyCat124
left a comment
There was a problem hiding this comment.
I added the few comments I managed today, I'm going to be off the rest of the day again but I'll try to finish the review off tomorrow.
| ''' PSyIR TreeSitter Fortran reader ''' | ||
| ''' | ||
|
|
||
| PSyIR fronted to ingest Fortran using the TreeSitter parse generator. |
There was a problem hiding this comment.
s/fronted/frontend
Also not sure "ingest" is a good word? But will leave that decision to yhou.
There was a problem hiding this comment.
Updated this line.
| https://github.com/stadelmanma/tree-sitter-fortran/blob/master/grammar.js | ||
|
|
||
| Note that psyclone is pinned to a particular version of treesitter (in | ||
| pyproject.toml), use that branch instead of master to follow the grammar. |
There was a problem hiding this comment.
Do we have a plan on how we keep up-to-date with treesitter? How fast moving is the grammar?
There was a problem hiding this comment.
Their grammar seems relatively stable nowadays, I would just update when we need a new feature.
I decided that pinning to a particular version (similar to what we do with fparser), to have a fixed grammar, and avoid lots of defensive checks that we would need to do if we cannot guarantee the exact children of each tsnode.
| tsnode: Optional['TSNode'], types: Union[str, Container[str]] | ||
| ) -> Generator['TSNode']: | ||
| ''' Provides a generator to iterate over the provided tsnode | ||
| chidlren of the given type(s). |
| ) | ||
| children.append(code_block) | ||
| return None | ||
| if expect is not _NodeExpectation.LIST: |
There was a problem hiding this comment.
Can we do this check at the start of the routine instead since it doesn't rely on the result at all.
LonelyCat124
left a comment
There was a problem hiding this comment.
I put a couple more comments - I'll have to review the rest later this week or next week when back to 100% to make sure I'm not missing anything.
| ''' | ||
| check_types = (types,) if isinstance(types, str) else types | ||
| if tsnode: | ||
| for child in tsnode.children: |
There was a problem hiding this comment.
I'm not sure what the best way to do yield functions is - should the bulk of the cost be on the first call or is it expected for each call to be similar? Since you could do
for child in [child for child in tsnode.children if child.type in check_types]:
yield child
And have all the computation's cost be at the start? Not sure which is better.
There was a problem hiding this comment.
Actually I don't even know if list comprehensions are done lazily with that or not...
There was a problem hiding this comment.
I googled a bit, so the list comprehension is not done lazily. If we did a generator statement (with ( instead of [) then it would be done lazily (and thus be similar to your current implementation) so that suggests to me that we should make the generator be lazy and not precompute it, so happy to leave this as is if you are.
There was a problem hiding this comment.
Yes, the idea of the generator was precisely to not precompute things so when we search a tree we stop at the instance found instead of iterating the whole tree.
|
@sergisiso One comment I do have is that it might be worth splitting the tests up into more files (by input/output nodes) - I don't like having one large "generic test" file for treesitter like we have for fparser, I think some more modular would make it much easier. |
LonelyCat124
left a comment
There was a problem hiding this comment.
@sergisiso I didn't go through the tests yet, but I did finish fortran_tree_reader.py now. Most of the comments are either questioning the input source to reach certain failure states, or poor documentation or typehinting.
| ) | ||
| return code_block | ||
|
|
||
| def _get_handler(self, tsnode: 'TSNode') -> Callable: |
There was a problem hiding this comment.
Return type question here - We reach the end of this function and not return anything. First should we explicitly return None (so we always have coverage)? Return type should be Callable | None or Optional as you prefer.
| def _translation_unit(self, tsnode: 'TSNode') -> nodes.Node: | ||
| # If at this point we still don't have a handler, it is unsupported | ||
| raise NotImplementedError( | ||
| f"Unsupported '{tsnode.type}' tree-sitter node.") from None |
There was a problem hiding this comment.
Why does this raise from None?
|
|
||
| def _translation_unit_handler( | ||
| self, tsnode: 'TSNode' | ||
| ) -> nodes.Node: |
There was a problem hiding this comment.
returns a FileContainer not just nodes.Node - probably worth being specific wherever we can.
| def _module_handler(self, tsnode: 'TSNode') -> nodes.Node: | ||
| def _module_handler( | ||
| self, tsnode: 'TSNode' | ||
| ) -> nodes.Node: |
| with self._using_scope(container.symbol_table): | ||
| visibility_map = self._process_access_statements(tsnode.children) | ||
|
|
||
| # This nodes are already processed |
There was a problem hiding this comment.
Can you add what processes these to the comment?
There was a problem hiding this comment.
The comment isn't quite accurate either as internal_procedures are handled after, not already processed.
| return nodes.IntrinsicCall.create(intrinsic, args) | ||
| except (TypeError, ValueError): | ||
| raise NotImplementedError( | ||
| f"Unsupported operands for {intrinsic.name}") from None |
There was a problem hiding this comment.
Test from this requires monkeypatching, is this actually reachable code we want to create a Codeblock for?
| ''' | ||
| ident = next(children_of_type(tsnode, "identifier"), None) | ||
| if ident is None: | ||
| raise NotImplementedError( |
There was a problem hiding this comment.
What happens if we just allow this?
| if before: | ||
| lower = self._process_nodes( | ||
| before[0], _NodeExpectation.EXPRESSION) | ||
| if not after: |
There was a problem hiding this comment.
Is this valid input source?
There was a problem hiding this comment.
Split the tests please, I've not otherwise reviewed them yet.
There was a problem hiding this comment.
Generally I'd like more comments/docstring explanations, A lot of the code is fairly understandable, but there is little discussion of the treesitter structure that it relies on, which is useful both for developers to understand from this file, but also makes it easier to diagnose issues if treesitter structures were to change in the future.
LonelyCat124
left a comment
There was a problem hiding this comment.
@sergisiso I didn't go through the tests yet, but I did finish fortran_tree_reader.py now. Most of the comments are either questioning the input source to reach certain failure states, or poor documentation or typehinting.
This PR focuses on extending the number of treesitter nodes that are recognised and handled, and adds many small Fortran snippets in the tests that are already parsable, but it is still: