From 9cc670e42f632028039676021a6a438d045cfdd2 Mon Sep 17 00:00:00 2001 From: Steve Witten Date: Sun, 14 Oct 2018 15:27:48 -0700 Subject: [PATCH 1/5] Minor tweaks to allow subclasses Several examples of Regexify subclasses are included --- examples/example.rb | 235 ++++++++++++++++++++++++++++++++++++++++++++ lib/regexify.rb | 19 +++- 2 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 examples/example.rb diff --git a/examples/example.rb b/examples/example.rb new file mode 100644 index 0000000..432f9a0 --- /dev/null +++ b/examples/example.rb @@ -0,0 +1,235 @@ +#!/usr/bin/env ruby + +load '../lib/regexify.rb' + +# monkey patch Regexify to add 'to_s' method + +class Regexify + public + def to_s + @str + end +end + +class RegexifyWithOptions < Regexify + + @options = nil + + public + + attr_accessor :options + + def initialize(opt = nil) + super() + raise Regexify::Error.new("Illegal regex modifier string") \ + unless opt.kind_of?(NilClass) || \ + (opt.kind_of?(String) && opt.match?(/[ixm]/)) + @options = opt + end + + # Converts Regexify object to Regexp + def regex + opts = 0 + unless @options.nil? + @options = squeeze(@options) + @options.each_char { |c| + case c + when 'i' + opts |= Regexp::IGNORECASE + when 'x' + opts |= Regexp::EXTENDED + when 'm' + opts |= Regexp::MULTILINE + end + } + end + Regexp.new(@str, opts) + end + + protected + + def squeeze(str) + uniqs = '' + str.sub(/\s+/,'').each_char { |s| uniqs += s if !uniqs.include?(s) } + return uniqs.downcase + end +end + +class RegexifyWithAnchor < Regexify + + public + + def initialize + super + end + +# Insert an anchor: +# +# \A - Matches beginning of string. +# \Z - Matches end of string. If string ends with a newline, it matches just +# before newline +# \z - Matches end of string +# \G - Matches point where last match finished +# \b - Matches word boundaries when outside brackets; backspace (0x08) when +# inside brackets +# \B - Matches non-word boundaries + + def anchor(a) + raise Regexify::Error.new("Illegal anchor.") \ + unless a.length == 1 && a.match?(/[AZzGbB]/) + @str += "\\#{a}" + self + end + +# (?=pat) - Positive lookahead assertion: ensures that the following characters +# match pat, but doesn't include those characters in the matched text + + def pos_look_ahead(pat) + @str += "\(\?=#{pat}\)" + self + end + +# (?!pat) - Negative lookahead assertion: ensures that the following characters +# do not match pat, but doesn't include those characters in the +# matched text + + def neg_look_ahead(pat) + @str += "\(\?!#{pat}\)" + self + end + +# (?<=pat) - Positive lookbehind assertion: ensures that the preceding +# characters match pat, but doesn't include those characters in the +# matched text + + def pos_look_behind(pat) + @str += "\(\?<=#{pat}\)" + self + end + +# (? e1 + puts "\t4. " + "Regexify::Error => #{e1}" +end +puts "" +begin + t = RegexifyWithOptions.new(1) +rescue Regexify::Error => e2 + puts "\t5. " + "Regexify::Error => #{e2}" +end + + +puts "" +puts "RegexifyWithAnchor" +puts "" + +puts "\t" + RegexifyWithAnchor.new + .begin_with(:uppercase, exactly: 3) + .then(:number, '-', range: [2,10]) + .not(:alphanumeric, exactly:1) + .anchor('b') + .pos_look_ahead("boy") + .anchor('Z') + .neg_look_behind("howdy") + .end_with('!').to_s + +puts "" +puts "RegexifyANSI" +puts "" + +puts "\t" + RegexifyANSI.new + .begin_with(:uppercase, exactly: 3) + .then(:number, '-', range: [2,10]) + .not(:alphanumeric, exactly:1) + .then(:hexdigit) + .then(:punctuation) + .end_with('!').to_s + +puts "" + diff --git a/lib/regexify.rb b/lib/regexify.rb index 956669d..78301bb 100644 --- a/lib/regexify.rb +++ b/lib/regexify.rb @@ -1,9 +1,13 @@ # Where the regex magic happens class Regexify + public + # Class to raise errors class Error < StandardError; end + private + # a small list of popular regex tokens that are available with regexify PATTERNS = { number: '0-9', @@ -20,9 +24,19 @@ class Error < StandardError; end # chars that needs to be escaped in a regex ESCAPED_CHARS = %w(* . ? ^ + $ | ( ) [ ] { } \\) + protected + + # instance variables + @str = nil + @patterns = nil + @complete = nil + + public + # default constructor def initialize @str = "" + @patterns = PATTERNS @complete = false end @@ -99,8 +113,8 @@ def parse(args, exactly: nil, range: nil) def extract_regex(arg) regex_str = "" if arg.is_a? Symbol - raise Regexify::Error.new('symbol not defined in patterns') unless PATTERNS.key?(arg) - PATTERNS[arg] + raise Regexify::Error.new('symbol not defined in patterns') unless @patterns.key?(arg) + @patterns[arg] else escape(arg) end @@ -142,4 +156,5 @@ def contains_symbols?(args) end return false end + end From 959775e2229782853a727737128712a8a581a8ab Mon Sep 17 00:00:00 2001 From: Steve Witten Date: Sun, 14 Oct 2018 15:49:18 -0700 Subject: [PATCH 2/5] added accessor methods for 'options' Added accessor methods with validated input for 'options' in RegexifyWithOptions --- examples/example.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/example.rb b/examples/example.rb index 432f9a0..c1cff2c 100644 --- a/examples/example.rb +++ b/examples/example.rb @@ -17,7 +17,16 @@ class RegexifyWithOptions < Regexify public - attr_accessor :options + def options + @options + end + + def options=(opt) + raise Regexify::Error.new("Illegal regex modifier string") \ + unless opt.kind_of?(NilClass) || \ + (opt.kind_of?(String) && opt.match?(/[ixm]/)) + @options = opt + end def initialize(opt = nil) super() From d644d055aee060f8b27afb42ff8df8c22bdef403 Mon Sep 17 00:00:00 2001 From: Steve Witten Date: Sun, 14 Oct 2018 15:57:35 -0700 Subject: [PATCH 3/5] Removed redundant code Removed some redundant code --- examples/example.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/example.rb b/examples/example.rb index c1cff2c..80bdf91 100644 --- a/examples/example.rb +++ b/examples/example.rb @@ -30,10 +30,7 @@ def options=(opt) def initialize(opt = nil) super() - raise Regexify::Error.new("Illegal regex modifier string") \ - unless opt.kind_of?(NilClass) || \ - (opt.kind_of?(String) && opt.match?(/[ixm]/)) - @options = opt + self.options = opt end # Converts Regexify object to Regexp From b5b9f7d0e68ad534771eb8efde72fe6521944a3d Mon Sep 17 00:00:00 2001 From: Steve Witten Date: Sun, 14 Oct 2018 16:01:37 -0700 Subject: [PATCH 4/5] Fixed some indentation --- examples/example.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/example.rb b/examples/example.rb index 80bdf91..39ae0f0 100644 --- a/examples/example.rb +++ b/examples/example.rb @@ -148,10 +148,10 @@ def initialize # @patterns[:number] = '[[:digit:]]' @patterns[:uppercase] = '[[:upper:]]' - @patterns[:lowercase] = '[[:lower:]]' - @patterns[:letter] = '[[:alpha:]]' - @patterns[:alphanumeric] = '[[:alnum:]]' - @patterns[:whitespace] = '[[:space:]]' + @patterns[:lowercase] = '[[:lower:]]' + @patterns[:letter] = '[[:alpha:]]' + @patterns[:alphanumeric] = '[[:alnum:]]' + @patterns[:whitespace] = '[[:space:]]' # new patterns # From 6592cc6f9cd6050870f827bac92d1ff1433f8310 Mon Sep 17 00:00:00 2001 From: Steve Witten Date: Mon, 15 Oct 2018 11:54:21 -0700 Subject: [PATCH 5/5] Update example.rb --- examples/example.rb | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/examples/example.rb b/examples/example.rb index 39ae0f0..af0fcdf 100644 --- a/examples/example.rb +++ b/examples/example.rb @@ -38,16 +38,9 @@ def regex opts = 0 unless @options.nil? @options = squeeze(@options) - @options.each_char { |c| - case c - when 'i' - opts |= Regexp::IGNORECASE - when 'x' - opts |= Regexp::EXTENDED - when 'm' - opts |= Regexp::MULTILINE - end - } + opts |= Regexp::IGNORECASE if @options.match?(/i/) + opts |= Regexp::EXTENDED if @options.match?(/x/) + opts |= Regexp::MULTILINE if @options.match?(/m/) end Regexp.new(@str, opts) end @@ -56,8 +49,8 @@ def regex def squeeze(str) uniqs = '' - str.sub(/\s+/,'').each_char { |s| uniqs += s if !uniqs.include?(s) } - return uniqs.downcase + str.sub(/\s+/,'').each_char { |s| uniqs += s if !uniqs.include?(s) } + return uniqs.downcase end end