Conversation
|
I could also not find an official syntax for a linemaker, but looked at https://git.cels.anl.gov/ksalapenades/llvm-project/-/blob/develop/clang/test/Preprocessor/line-directive.c (and did some compiler tests to verify what gets rejected) |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #498 +/- ##
=======================================
Coverage 92.21% 92.21%
=======================================
Files 87 87
Lines 13832 13845 +13
=======================================
+ Hits 12755 12767 +12
- Misses 1077 1078 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Checking the git log, it appears this code was contributed by @reuterbal - I wonder if you would have time to provide some feedback? Thanks! |
reuterbal
left a comment
There was a problem hiding this comment.
Happy to provide my 2ct here. I agree that linemarkers should be handled like preprocessor directives and the implementation itself looks good to me.
As for the need for the additional regex validation: I believe you only need to encode the full linemarker specification as a regex pattern to use in WORDClsBase.match - either manually or using the building blocks in pattern_tools - e.g., like here:
fparser/src/fparser/two/pattern_tools.py
Line 416 in 5e124e5
Without testing it, I would expect this would maybe need to look like something like this:
_pattern = r"^\s*#\s+" + digit_string + "\s+" + file_name
| "line_ref", | ||
| [ | ||
| ('# 123 "file"', '# 123 "file"'), | ||
| (' # 123 "file" ', '# 123 "file"'), | ||
| ('# 123 "file" 1 3', '# 123 "file" 1 3'), | ||
| ], | ||
| ) | ||
| def test_linemarker(line_ref): | ||
| """Test that #line is recognized""" | ||
| line, ref = line_ref |
There was a problem hiding this comment.
NB: you can directly specify the parametrization as multiple variables:
| "line_ref", | |
| [ | |
| ('# 123 "file"', '# 123 "file"'), | |
| (' # 123 "file" ', '# 123 "file"'), | |
| ('# 123 "file" 1 3', '# 123 "file" 1 3'), | |
| ], | |
| ) | |
| def test_linemarker(line_ref): | |
| """Test that #line is recognized""" | |
| line, ref = line_ref | |
| "line, ref", | |
| [ | |
| ('# 123 "file"', '# 123 "file"'), | |
| (' # 123 "file" ', '# 123 "file"'), | |
| ('# 123 "file" 1 3', '# 123 "file" 1 3'), | |
| ], | |
| ) | |
| def test_linemarker(line, ref): | |
| """Test that #line is recognized""" |
There was a problem hiding this comment.
Oh yes, I totally forgot about that. Thanks!
|
|
||
| # The match method will check that it is a valid linemarker, i.e. | ||
| # it has a line number, and file name in double quotes. | ||
| _pattern = pattern.Pattern("<linemarker>", r"^\s*#", value="#") |
There was a problem hiding this comment.
Note that most Fortran compilers don't accept white space in front of the # for preprocessor directives. Are they more forgiving for line markers?
But since fparser allows for white space in front of directives, it's probably wise to allow them for marker as well.
There was a problem hiding this comment.
I actually remember that I was wondering about the \s at the beginning (since I did some testing originally, and knew it's not accepted). But since all regex here started with #\s* .. I just followed the trend :) And I'll agree to leave it in for consistency (and because I am afraid of breaking something).
| """ | ||
| Linemarker | ||
|
|
||
| linemarker-stmt is # digit-sequence [ "s-char-sequence" ] [digit ...] |
There was a problem hiding this comment.
This suggests the filename is optional - if that is the case, then the test parameterisation should be expanded by such a case.
There was a problem hiding this comment.
I re-checked it - it's hard to find the official syntax - there is https://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp_9.html, but it's not really the proper grammar, e.g. it does not specify the quotes, and as such it's not clear if it must be double quotes, or can be single quotes. FWIW, I tested it, and it appears it must be double quote.
Supporting an optional filename is a gnu extension. And as such, we shouldn't support it. I've updated the comment accordingly.
…er syntax. Extend match function to be able to return the fully matched string.
|
Hi Balthasar, thanks a lot for the quick review, really appreciated.
I am glad that I made the right decision here :)
I tried that, and thanks a lot for the example. Looking at the But then I am stuck with my original problem: I am using: But if I do this, the matching in Basically, the rest of the match is discarded (line number and filename) :( I tried to fix that using: So, I provide This passed nearly all tests, except it would not remove spaces between "#" and line number and line number and file name. Therefore, I adjusted one linemarker test (but the test still checks that leading spaces before the I am not sure if this is the right solution tbh :( Maybe it should not be based on @reuterbal , @arporter , @sergisiso - feedback appreciated! The modification of the |
reuterbal
left a comment
There was a problem hiding this comment.
Nice work, the details of the keyword/value returns were lost on me and your proposal seems sensible to me.
Overall I think that's indeed much better now, no further comments!
arporter
left a comment
There was a problem hiding this comment.
Looks good to me Joerg, thanks. I could do with a bit of help understanding some of the changes - probably because I know pretty much nothing about the Pattern class.
I think it would also be good to test the new functionality in context - i.e. provide some Fortran containing some linemarkers and check that things work OK.
|
|
||
| """C99 Preprocessor Syntax Rules.""" | ||
| """C99 Preprocessor Syntax Rules. It also supports linemarker statements | ||
| (which are technically not preprocessor directives, but are very close |
There was a problem hiding this comment.
Please also update the documentation (https://fparser.readthedocs.io/en/latest/fparser2.html#preprocessing-directives)
|
|
||
| class Cpp_Linemarker_Stmt(WORDClsBase): # Linemarker | ||
| """ | ||
| Linemarker |
There was a problem hiding this comment.
A bit more detail would be good here. i.e. explain what a Linemarker is.
|
|
||
| :param str string: the string to match with as a line statement. | ||
|
|
||
| :return: a tuple of size 1 with the right hand side as a string, \ |
There was a problem hiding this comment.
No line-continuation and please update to use type hints.
| @staticmethod | ||
| def match(string): | ||
| """Implements the matching for a linemarker. | ||
| The right hand side of the directive is not matched any further |
There was a problem hiding this comment.
What does the "right hand side of the directive" mean?
| require_cls=False, | ||
| ) | ||
|
|
||
| def tostr(self): |
| Returns the line marker as string. Note that fparser accepts | ||
| spaces before the `#`, but it should remove the spaces, hence | ||
| we lstrip the result | ||
| :return: this linemarker as a string. |
There was a problem hiding this comment.
Blank line before :return: please.
| return None | ||
| line = string[len(my_match.group()) :] | ||
| pattern_value = keyword.value | ||
| # If no constant return value is defined, |
There was a problem hiding this comment.
I need a bit of help here: what does "no constant return value" mean?
Fixes #390 by supporting linemarkers (
# 123 "test.f90"- indicating line number and file).I have used the existing preprocessor directive support, since linemarkers looks pretty much like a preprocessor directive (starting with
#). But I was unable to get it to verify properly out of the box - I feel like the existing implementation assumes that thevalueis constant (e.g.#ifdef), while in the case of a linemarker we don't have this (since it would need the line number and filename to verify correctness, and I couldn't work out how to do this). While it worked this way, it was unable to detect invalid linemarkers (# abc "xx.f90"or# 123 'no_single_quote_allowed.f90'). So, instead I added a simple regex check at the beginning, and only if that passes, it will use the established path.I am happy to reimplement (I know there is
pattern_tools.digit_stringandfile_name... I just couldn't figure out how to plug this all together.