+# Extensions to the Regexp class, with some common and/or complex regular
+# expressions.
+#
+class ::Regexp
+
+ # A method to build a regexp that matches a list of something separated by
+ # optional commas and/or the word "and", an optionally repeated prefix,
+ # and whitespace.
+ def Regexp.new_list(reg, pfx = "")
+ if pfx.kind_of?(String) and pfx.empty?
+ return %r(#{reg}(?:,?(?:\s+and)?\s+#{reg})*)
+ else
+ return %r(#{reg}(?:,?(?:\s+and)?(?:\s+#{pfx})?\s+#{reg})*)
+ end
+ end
+
+ IN_ON = /in|on/
+
+ # We start with some IRC related regular expressions, used to match
+ # Irc::User nicks and Irc::Channel names
+ #
+ # For each of them we define three versions of the regular expression:
+ # * a generic one, which should match for any server but may turn out to
+ # match more than a specific server would accept
+ # * an RFC-compliant matcher
+ # * TODO a server-specific one that uses the Irc::Server#supports method to build
+ # a matcher valid for a particular server.
+ #
+ module Irc
+ CHAN_FIRST = /[#&+]/
+ CHAN_SAFE = /![A-Z0-9]{5}/
+ CHAN_ANY = /[^\x00\x07\x0A\x0D ,:]/
+ GEN_CHAN = /(?:#{CHAN_FIRST}|#{CHAN_SAFE})#{CHAN_ANY}+/
+ RFC_CHAN = /#{CHAN_FIRST}#{CHAN_ANY}{1,49}|#{CHAN_SAFE}#{CHAN_ANY}{1,44}/
+
+ CHAN_LIST = Regexp.new_list(GEN_CHAN)
+
+ # Match "in #channel" or "on #channel" and/or "in private" (optionally
+ # shortened to "in pvt"), returning the channel name or the word 'private'
+ # or 'pvt' as capture
+ IN_CHAN = /#{IN_ON}\s+(#{GEN_CHAN})|(here)|/
+ IN_CHAN_PVT = /#{IN_CHAN}|in\s+(private|pvt)/
+
+ # As above, but with channel lists
+ IN_CHAN_LIST_SFX = Regexp.new_list(/#{GEN_CHAN}|here/, IN_ON)
+ IN_CHAN_LIST = /#{IN_ON}\s+#{IN_CHAN_LIST_SFX}|anywhere|everywhere/
+ IN_CHAN_LIST_PVT_SFX = Regexp.new_list(/#{GEN_CHAN}|here|private|pvt/, IN_ON)
+ IN_CHAN_LIST_PVT = /#{IN_ON}\s+#{IN_CHAN_LIST_PVT_SFX}|anywhere|everywhere/
+
+ SPECIAL_CHAR = /[\x5b-\x60\x7b-\x7d]/
+ NICK_FIRST = /#{SPECIAL_CHAR}|[[:alpha:]]/
+ NICK_ANY = /#{SPECIAL_CHAR}|[[:alnum:]]|-/
+ GEN_NICK = /#{NICK_FIRST}#{NICK_ANY}+/
+ RFC_NICK = /#{NICK_FIRST}#{NICK_ANY}{0,8}/