Skip to content

Fix parsing parenthesized xpath#322

Open
tompng wants to merge 2 commits into
ruby:masterfrom
tompng:parenthesized_xpath
Open

Fix parsing parenthesized xpath#322
tompng wants to merge 2 commits into
ruby:masterfrom
tompng:parenthesized_xpath

Conversation

@tompng
Copy link
Copy Markdown
Member

@tompng tompng commented Jun 3, 2026

Fixes #316

Also fixes bug parsing (1+2)*3

REXML::Parsers::XPathParser.new.parse("(a|b)/c")
#=> REXML::ParseException (before)
#=> [:group, [:union, [:child, :qname, "", "a"], [:child, :qname, "", "b"]], :child, :qname, "", "c"] (after)

REXML::Parsers::XPathParser.new.parse("(1+2) * 3")
#=> [:mult, [:plus, [:literal, 1], [:literal, 2]], [:literal, 3]]
REXML::Parsers::XPathParser.new.parse("(1+2)*3")
#=> REXML::ParseException (before)
#=> [:mult, [:plus, [:literal, 1], [:literal, 2]], [:literal, 3]] (after)

Source code comment:

#| LocationPath
#| FilterExpr ('/' | '//') RelativeLocationPath 

Actual implementation was:

#| LocationPath
#| FilterExpr (RelativeLocationPath if this_part.start_with?('/'))
#| FilterExpr (RelativeLocationPath if this_part.start_with?('/')) LocationPath
#| FilterExpr LocationPath
# Note that RelativeLocationPath never accepts string starting with `/`

With this pull request, the implementation will match to its source code comment.

Copilot AI review requested due to automatic review settings June 3, 2026 19:02
@tompng tompng changed the title Parenthesized xpath Fix parsing parenthesized xpath Jun 3, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds coverage and parser tweaks for XPath expressions involving parentheses, unions, and ambiguous * spacing, improving consistency when whitespace is present/absent.

Changes:

  • Add XPath evaluation tests for parenthesized union expressions followed by / and //.
  • Add XPathParser tests to ensure multiplication operator parsing is whitespace-insensitive.
  • Adjust PathExpr parsing to handle parenthesized/filter expressions followed by location steps.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
test/xpath/test_base.rb Adds evaluation tests for parenthesized XPath union expressions with subsequent path steps.
test/parser/test_xpath.rb Adds parser-level tests for * operator behavior with/without spaces.
lib/rexml/parsers/xpathparser.rb Updates PathExpr to better parse filter/parenthesized expressions followed by / or //.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/rexml/parsers/xpathparser.rb
Comment on lines +597 to +605
if /\A\s*\//.match?(rest)
rest = rest.lstrip
if rest[1] == ?/
n << :descendant_or_self
n << :node
rest = rest[2..-1]
else
rest = rest[1..-1]
end
Comment thread test/parser/test_xpath.rb Outdated
Comment on lines +115 to +126
def test_mult_and_spaces
parser = REXML::Parsers::XPathParser.new
assert_equal(parser.parse("1 * 2 * 3"), parser.parse("1*2*3"))
assert_equal(parser.parse("a[( ( 1 + 2 ) * 3 + 4 * ( 5 + 6 ) ) * 7 < 8]"), parser.parse("a[((1+2)*3+4*(5+6))*7<8]"))
# number(a/b) * 2
assert_equal(parser.parse("(a/b) * 2"), parser.parse("(a/b)*2"))
assert_equal(parser.parse("a/b * 2"), parser.parse("a/b*2"))
# number(a/b/*) * 2
assert_equal(parser.parse("a/b/* * 2"), parser.parse("a/b/**2"))
# number(*) * number(*/*) * number(*)
assert_equal(parser.parse("* * */* * *"), parser.parse("***/***"))
end
Copilot AI review requested due to automatic review settings June 4, 2026 10:42
@tompng tompng force-pushed the parenthesized_xpath branch from 5b1e3e1 to 8031c92 Compare June 4, 2026 10:42
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment on lines +593 to 602
if /\A\s*\//.match?(rest)
rest = rest.lstrip
if rest.start_with?('//')
n << :descendant_or_self
n << :node
rest = rest[2..-1]
else # starts with '/'
rest = rest[1..-1]
end
rest = RelativeLocationPath(rest, n)
Comment on lines +590 to +592
if rest == path
rest = LocationPath(rest, n)
else
Comment thread test/parser/test_xpath.rb
)
end

def test_mult_asterisk_without_surrounding_spaces
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.

REXML::Parsers::XPathParser can't parse some xpath with parentheses

2 participants