ronin-rb/ronin-support

View on GitHub
lib/ronin/support/text/typo.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true
#
# Copyright (c) 2006-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-support is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-support is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-support.  If not, see <https://www.gnu.org/licenses/>.
#

require 'ronin/support/text/typo/generator'

module Ronin
  module Support
    module Text
      #
      # Generates typos in words.
      #
      # ## Core-Ext Methods
      #
      # * {String#each_typo}
      # * {String#typo}
      # * {String#typos}
      #
      # @api public
      #
      # @since 1.0.0
      #
      module Typo
        # Typo generator that repeats characters.
        OMIT_CHARS = Generator[
          [/(?<=\w)ae(?=\w)/, 'e'],
          [/(?<=\w)dd(?=\w)/, 'd'],
          [/(?<=\w)rr(?=\w)/, 'r'],
          [/(?<=\w)ll(?=\w)/, 'l'],
          [/(?<=\w)mm(?=\w)/, 'm'],
          [/(?<=\w)ss(?=\w)/, 's'],
          [/(?<=\w)oo(?=\w)/, 'o'],
          [/(?<=\w)ous/,  'us'],
          [/(?<=\w)ous/,  'os'],
          [/(?<=\w)ious/, 'uos'],
          [/(?<=\w)ious/, 'ios'],
          [/[_-]/, '']
        ]

        # Typo generator that repeats characters.
        REPEAT_CHARS = Generator[
          [/(?<=\w)o(?=\w)/, 'oo'],
          [/(?<=\w)l(?=\w)/, 'll'],
          [/(?<=\w)s(?=\w)/, 'ss']
        ]

        # Typo generator that swaps neighboring characters.
        SWAP_CHARS = Generator[
          [/(?<=\w)au(?=\w)/, 'ua'],
          [/(?<=\w)ua(?=\w)/, 'au'],
          [/(?<=\w)ie(?=\w)/, 'ei'],
          [/(?<=\w)ei(?=\w)/, 'ie'],
          [/(?<=\w)th(?=\w)/, 'ht'],
          [/(?<=\w)ht(?=\w)/, 'th'],
          [/(?<=\w)il(?=\w)/, 'li'],
          [/(?<=\w)ou(?=\w)/, 'uo']
        ]

        # Typo generator that swaps different symbols.
        SWAP_SYMBOLS = Generator[
          [/_/, '-'],
          [/-/, '_']
        ]

        # Typo generator that changes the suffix of words.
        CHANGE_SUFFIX = Generator[
          [/er$/,   'ar'],
          [/ar$/,   'er'],
          [/el$/,   'le'],
          [/al$/,   'al'],
          [/es$/,   's'],
          [/s$/,    ''],
          [/ent$/,  'ant'],
          [/ant$/,  'ent'],
          [/able$/, 'ible'],
          [/ible$/, 'able'],
          [/ence$/, 'ense'],
          [/ense$/, 'ence'],
          [/ance$/, 'anse'],
          [/anse$/, 'ance'],
          [/ier$/,  'er'],
          [/ion$/,  'on']
        ]

        # Default typo generator.
        #
        # @note Does not include the {SWAP_SYMBOLS} typo rules.
        DEFAULT = Generator.new(
          OMIT_CHARS.rules + REPEAT_CHARS.rules + SWAP_CHARS.rules +
          CHANGE_SUFFIX.rules
        )

        #
        # Typo generator that repeats characters.
        #
        # @return [Generator]
        #
        # @see OMIT_CHARS
        #
        def self.omit_chars
          OMIT_CHARS
        end

        #
        # Typo generator that repeats characters.
        #
        # @return [Generator]
        #
        # @see REPEAT_CHARS
        #
        def self.repeat_chars
          REPEAT_CHARS
        end

        #
        # Typo generator that swaps neighboring characters.
        #
        # @return [Generator]
        #
        # @see SWAP_CHARS
        #
        def self.swap_chars
          SWAP_CHARS
        end

        #
        # Typo generator that swaps different symbols.
        #
        # @return [Generator]
        #
        # @see SWAP_SYMBOLS
        #
        def self.swap_symbols
          SWAP_SYMBOLS
        end

        #
        # Typo generator that changes the suffix of words.
        #
        # @return [Generator]
        #
        # @see CHANGE_SUFFIX
        #
        def self.change_suffix
          CHANGE_SUFFIX
        end

        #
        # Builds a set of typo substitution rules.
        #
        # @param [Hash{Symbol => Boolean}] kwargs
        #   Additional typo options.
        #
        # @option kwargs [Boolean] :omit_chars
        #   Whether to enable/disable omission of repeated characters.
        #
        # @option kwargs [Boolean] :repeat_chars
        #   Whether to enable/disable repeatition of single characters.
        #
        # @option kwargs [Boolean] :swap_chars
        #   Whether to enable/disable swapping of certain common character
        #   pairs.
        #
        # @option kwargs [Boolean] :change_suffix
        #   Whether to enable/disable changing the suffixes of words.
        #
        # @return [Generator]
        #   The typo generator.
        #
        def self.generator(**kwargs)
          if kwargs.empty?
            DEFAULT
          else
            rules = []
            rules.concat(OMIT_CHARS.rules)    if kwargs[:omit_chars]
            rules.concat(REPEAT_CHARS.rules)  if kwargs[:repeat_chars]
            rules.concat(SWAP_CHARS.rules)    if kwargs[:swap_chars]
            rules.concat(SWAP_SYMBOLS.rules)  if kwargs[:swap_symbols]
            rules.concat(CHANGE_SUFFIX.rules) if kwargs[:change_suffix]

            Generator.new(rules)
          end
        end

        #
        # Returns a random typo substitution for the given word.
        #
        # @param [String] word
        #   The given String.
        #
        # @param [Hash{Symbol => Boolean}] kwargs
        #   Additional keyword arguments.
        #
        # @option kwargs [Boolean] omit
        #   Enables/disables omission of repeated characters.
        #
        # @option kwargs [Boolean] repeat
        #   Enables/disables repeatition of single characters.
        #
        # @option kwargs [Boolean] swap
        #   Enables/disables swapping of certain common character pairs.
        #
        # @option kwargs [Boolean] suffix
        #   Enables/disables changing the suffixes of words.
        #
        # @return [String]
        #   A random typo of the given word.
        #
        # @see String#typo
        #
        def self.substitute(word,**kwargs)
          generator(**kwargs).substitute(word)
        end

        #
        # Enumerates over every typo mistake for the given word.
        #
        # @param [String] word
        #   The given String.
        #
        # @param [Hash{Symbol => Boolean}] kwargs
        #   Additional keyword arguments.
        #
        # @option kwargs [Boolean] omit
        #   Enables/disables omission of repeated characters.
        #
        # @option kwargs [Boolean] repeat
        #   Enables/disables repeatition of single characters.
        #
        # @option kwargs [Boolean] swap
        #   Enables/disables swapping of certain common character pairs.
        #
        # @option kwargs [Boolean] suffix
        #   Enables/disables changing the suffixes of words.
        #
        # @yield [typoed]
        #   If a block is given, it will be passed each possible typo of the
        #   original String.
        #
        # @yieldparam [String]
        #   A modified version of the original String.
        #
        # @return [Enumerator]
        #   If no block is given, an Enumerator will be returned.
        #
        # @see String#each_typo
        # @see String#typos
        #
        def self.each_substitution(word,**kwargs,&block)
          generator(**kwargs).each_substitution(word,&block)
        end
      end
    end
  end
end

require 'ronin/support/text/typo/core_ext'