Fix anchors - #6
Conversation
StringScanner has a bug where it does not properly recognise anchors (like `\A`). The cause is a mistake in the value of the second argument passed to `onig_match` and `onig_search`. This fixes these values to be the beginning of the string, not the position of the cursor.
|
I need to update some tests. Will look into that. |
|
Needs more work. Withdrawn for the moment. |
|
I've updated the OP with a better explanation of the bug and how this fix works. |
|
Any response to this? It fixes a long-standing bug, passes all tests, is there any other information I can provide? |
|
I'm neutral for this change. |
|
Yes! As was mentioned, we have to hack around this in rouge because StringScanner cannot tell when the beginning of a line is. See https://github.com/rouge-ruby/rouge/blob/master/lib/rouge/regex_lexer.rb#L299 We basically have to look at the string representation of every regular expression, guess whether it starts with |
|
Thanks. |
|
I've merged with some modifications. |
pyrmont
left a comment
There was a problem hiding this comment.
One minor comment:
|
Apologies for posting on a closed pull request, but I believe this is missing a test. The new test only tests the happy case - that s = StringScanner.new("ab\nc")
assert_equal 1, s.skip(/^a/)
assert_nil s.skip(/^b/)
assert_equal 2, s.skip(/b\n/)
assert_equal 1, s.skip(/^c/)would resolve this by actually testing that |
|
(I am happy to open a PR for the above) |
|
I think that https://github.com/ruby/strscan/pull/6/files#diff-490aef8dd6acb090e22d2b9c436f5469R326 is a "not happy case". |
|
Unfortunately the example you referenced tests the behavior of The original bug caused the scanner head to be not only an anchor for |
|
I don't understand why you say the require "strscan"
s = StringScanner.new("a\nb")
p s.skip(/a\n/)
p s.skip(/\Ab/)Without this change: With this change: But I don't oppose your suggestion strongly. Because the new test will not increase test time and mainteinance cost so much. |
|
I will open a pull request. For posterity, this change makes In this case, I am suggesting we test that |
Six years ago, the Rouge library author, @jneen, reported that there was a bug in the handling of anchors in StringScanner. At the time, StringScanner did not have a maintainer and the view was expressed that this behaviour was intended.
The Bug
In short, the bug is that anchors in regular expressions don't work properly in StringScanner. This can be seen in the sample code below (taken from the original bug report):
The Cause
The cause of the bug is the way
strscan_do_scan()is implemented inext/strscan/strscan.c. This function calls out toonig_match()andonig_search()like this:strscan/ext/strscan/strscan.c
Lines 480 to 490 in cace20f
The reason that the anchors don't work is because the second argument to each method is
CURPTR(p). This tells Onigmo to treat the scan head as the beginning of the string to be searched. This is a mistake.The Fix
It is clear from the method definitions for
onig_match()andonig_search()(see here and here) that you can begin searching within a string by passing a pointer to the scan head as the fourth argument. The equivalent function used in the implementation ofRegexp's search methods (eg.Regexp#match) takes this approach: passing different pointers for the start of the string and the location to begin searching.We can do this in
strscan_do_scan():However, this change is not enough and will cause various tests in StringScanner's test suite to fail. Because StringScanner calls
onig_match()andonig_search()in the 'wrong way', it has to do various adjustments throughout the file to correct the values ofp->regsset by Onigmo. Once we remove these adjustments, the tests pass.The Result
This PR is not intended to be a breaking change. Although, it changes the implementation in
ext/strscan/strscan.c, the Ruby API returns the same results as it did before the change. Instead, this PR increases functionality by allowing users to use anchors in regular expressions.It also conforms with the principle of least surprise. A user of StringScanner should be able to use the same regular expressions they could use in
Regexp#matchand get the same results.Hopefully that all makes sense but please let me know if I can provide any more information.