From c746ed7dad2e4845b4ee7e44e4880fd1cec5704e Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Fri, 9 Feb 2018 08:36:43 -0700 Subject: [PATCH 1/2] allow parentheses to be explicitly escaped --- .../cucumber_expressions/cucumber_expression.rb | 13 +++++++++++-- .../cucumber_expression_spec.rb | 6 +++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cucumber-expressions/ruby/lib/cucumber/cucumber_expressions/cucumber_expression.rb b/cucumber-expressions/ruby/lib/cucumber/cucumber_expressions/cucumber_expression.rb index cdbe392ea4..43cde897d1 100644 --- a/cucumber-expressions/ruby/lib/cucumber/cucumber_expressions/cucumber_expression.rb +++ b/cucumber-expressions/ruby/lib/cucumber/cucumber_expressions/cucumber_expression.rb @@ -8,7 +8,8 @@ class CucumberExpression # Does not include (){} characters because they have special meaning ESCAPE_REGEXP = /([\\^\[$.|?*+\]])/ PARAMETER_REGEXP = /{([^}]+)}/ - OPTIONAL_REGEXP = /\(([^)]+)\)/ + # Parentheses will be double-escaped due to ESCAPE_REGEXP + OPTIONAL_REGEXP = /([\\][\\])?\(([^)]+)\)/ ALTERNATIVE_NON_WHITESPACE_TEXT_REGEXP = /([^\s^\/]+)((\/[^\s^\/]+)+)/ attr_reader :source @@ -19,10 +20,18 @@ def initialize(expression, parameter_type_registry) regexp = '^' match_offset = 0 + # This will cause explicitly-escaped parentheses to be double-escaped expression = expression.gsub(ESCAPE_REGEXP, '\\\\\1') # Create non-capturing, optional capture groups from parenthesis - expression = expression.gsub(OPTIONAL_REGEXP, '(?:\1)?') + expression = expression.gsub(OPTIONAL_REGEXP) do |match| + # look for double-escaped parentheses + if $1 == '\\\\' + "\\(#{$2}\\)" + else + "(?:#{$2})?" + end + end expression = expression.gsub(ALTERNATIVE_NON_WHITESPACE_TEXT_REGEXP) do |_| "(?:#{$1}#{$2.tr('/', '|')})" diff --git a/cucumber-expressions/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb b/cucumber-expressions/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb index e48b888fac..511a36ae0b 100644 --- a/cucumber-expressions/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb +++ b/cucumber-expressions/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb @@ -26,7 +26,7 @@ module CucumberExpressions it('matches multiple double quoted strings') do expect(match('three {string} and {string} mice', 'three "blind" and "crippled" mice')).to eq(['blind', 'crippled']) end - + it('matches single quoted string') do expect(match('three {string} mice', "three 'blind' mice")).to eq(['blind']) end @@ -55,6 +55,10 @@ module CucumberExpressions expect(match('three {string} mice', "three 'bl\\'nd' mice")).to eq(["bl'nd"]) end + it 'matches escaped parentheses' do + expect(match('three \\(exceptionally) {string} mice', 'three (exceptionally) "blind" mice')).to eq(['blind']) + end + it "matches int" do expect(match("{int}", "22")).to eq([22]) end From 00f859ff36ff71fb503605508e1c554f3cc40131 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Fri, 9 Feb 2018 08:48:01 -0700 Subject: [PATCH 2/2] update docs --- cucumber-expressions/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cucumber-expressions/README.md b/cucumber-expressions/README.md index c7adb77441..60de40cfcb 100644 --- a/cucumber-expressions/README.md +++ b/cucumber-expressions/README.md @@ -128,6 +128,16 @@ It would also match this text: I have 42 cucumbers in my belly +If you ever need to match those parentheses literally, you can escape the +first opening parenthesis with a backslash: + + I have {int} cucumber(s) in my belly \(amazing!) + +This expression would match the following examples: + + I have 1 cucumber in my belly (amazing!) + I have 42 cucumbers in my belly (amazing!) + ## Alternative text Sometimes you want to relax your language, to make it flow better. For example: