# The program conjugates most common english verbs with the following option: # * :tense => :past or :present or :future # * :person => :first or :second or :third # * :plurality => :singular or :plural # * :aspect => :habitual or :perfect or :perfective or :progressive or :prospective # * :mood => :indicative or :imperative or :subjunctive # Respective defaults are :present, :third, :singular, :habitual, and :indicative # # Author:: Andy Rossmeissl # Copyright:: Copyright (c) 2009 Andy Rossmeissl # License:: Found in LICENSE file module Verbs module Conjugator extend self # This class determines the conjugations from the given options (or defaults) # conjugations are then applied to the verb class Conjugations include Singleton # permit outside functions access to these variables attr_reader :irregulars, :single_terminal_consonants, :copulars # Creates initial variables for class def initialize @irregulars, @single_terminal_consonants, @copulars = {}, [], {} end # Determines irregular verbs from the expression # Params: # * infinitive, the given verb # * preterite, denote events that took place in the past # * past_participle, form of a verb, ending in 'ed' # * &blk, block of code that may be run def irregular(infinitive, preterite = nil, past_participle = nil, &blk) if block_given? # create new Verb object with infinitive and &blk irregular = ::Verbs::Verb.new infinitive, &blk else raise ArgumentError, "Standard irregular verbs must specify preterite and past participle forms" unless preterite and past_participle # create new Verb object with infinitive, preterite and past_participle irregular = ::Verbs::Verb.new infinitive, :preterite => preterite, :past_participle => past_participle end @irregulars[infinitive] = irregular end # Find single terminal consonant with the infinitive # Params: # * infinitive, the given verb def single_terminal_consonant(infinitive) @single_terminal_consonants << infinitive end end # Runs a block of code if given in a class instance # else only class instance is created def conjugations if block_given? yield Conjugations.instance else Conjugations.instance end end # Using options given, determine the conjugation and the subject # Return the subject, if there is one, and the proper conjugation # Params: # * infinitive, the given verb # * options, the list of parameters to alter the conjugation def conjugate(infinitive, options = {}) infinitive = infinitive.dup if infinitive.is_a?(String) # set all options according to parameter, or the default tense = options[:tense] || :present # present, past, future person = options[:person] || :third # first, second, third plurality = options[:plurality] || :singular # singular, plural diathesis = options[:diathesis] || :active # active, passive mood = options[:mood] || :indicative # imperative, subjunctive aspect = options[:aspect] || :habitual # perfective, habitual, progressive, perfect, prospective check_for_improper_constructions(tense, person, mood) # find incompatabilities form = form_for(tense, aspect) # find form array based on tense and aspect # map form array to conjugation array, applying infinitive and options to the array conjugation = form.map { |e| resolve e, infinitive, tense, person, plurality, mood }.join(' ').strip if options[:subject] # When options includes a subject, actor = options.delete(:subject) # remove from options and make subject humanized actor = subject(options).humanize if actor.is_a?(TrueClass) end "#{actor} #{conjugation}".strip end # Finds the pronoun associated with the subject for the conjugation # Returns the pronoun # Params: # * options, list of options given to determine conjugation def subject(options) case [options[:person], options[:plurality]] when [:first, :singular] 'I' when [:first, :plural] 'we' when [:second, :singular], [:second, :plural] 'you' when [:third, :singular] 'he' when [:third, :plural] 'they' end end private # Resolves conflictions between options of the conjugation # Params: # * element, # * infinitive, the given verb # * tense, an option given by the user # * person, an option given by the user # * plurality, an option given by the user # * mood, an option given by the user def resolve(element, infinitive, tense, person, plurality, mood) case element when String element when :infinitive infinitive when :present, :past, :present_participle, :past_participle inflect infinitive, element, person, plurality, mood when Symbol inflect element, tense, person, plurality, mood end end # Change the form to express the proper grammatical function # Params: # * infinitive,the given verb # * inflection, form to be changed # * person, an option given by the user # * plurality, an option given by the user # * mood, an option given by the user def inflect(infinitive, inflection, person, plurality, mood) send(*([inflection, infinitive, person, plurality, mood][0, method(inflection).arity + 1])) end def present(infinitive, person, plurality, mood) if verb = conjugations.irregulars[infinitive] conjugate_irregular(verb, :tense => :present, :person => person, :plurality => plurality, :mood => mood) elsif person == :third and plurality == :singular and not mood == :subjunctive present_third_person_singular_form_for infinitive else infinitive end end # Conjugate verb to past with relevent options determining outcome # Params: # * infinitive, the given verb # * person, the subject of the verb # * plurality, an option given by the user # * mood, an option given by the user def past(infinitive, person, plurality, mood) if verb = conjugations.irregulars[infinitive] conjugate_irregular(verb, :tense => :past, :person => person, :plurality => plurality, :mood => mood) else regular_preterite_for infinitive end end # Forming verb to apply present tense endings # Params: # * infinitive, the given verb def present_participle(infinitive) if infinitive.to_s.match(/#{CONSONANT_PATTERN}#{VOWEL_PATTERN}#{CONSONANT_PATTERN}$/) and !conjugations.single_terminal_consonants.include?(infinitive.to_sym) present_participle_with_doubled_terminal_consonant_for infinitive elsif infinitive.to_s.match(/c$/) infinitive.to_s.concat('king').to_sym elsif infinitive.to_s.match(/^be$/) or infinitive.to_s.match(/ye$/) or infinitive.to_s.match(/oe$/) or infinitive.to_s.match(/nge$/) or infinitive.to_s.match(/ee$/) infinitive.to_s.concat('ing').to_sym elsif infinitive.to_s.match(/ie$/) infinitive.to_s[0..-2].concat('ying').to_sym elsif infinitive.to_s.match(/e$/) infinitive.to_s[0..-2].concat('ing').to_sym else infinitive.to_s[0..-1].concat('ing').to_sym end end # Forming verb to apply past tense endings # Params: # * infinitive, the given verb def past_participle(infinitive) if verb = conjugations.irregulars[infinitive] conjugate_irregular(verb, :tense => :past, :derivative => :participle) else regular_preterite_for infinitive end end # # Params: # * verb, # * options, def conjugate_irregular(verb, options) return verb[options] if verb[options] tense = options[:tense] person = options[:person] plurality = options[:plurality] derivative = options[:derivative] if [tense, person, plurality] == [:present, :third, :singular] present_third_person_singular_form_for verb elsif [tense, derivative] == [:past, :participle] verb.past_participle elsif tense == :present verb.infinitive elsif tense == :past verb.preterite end end # Apply thir person rules to the verb for the conjugation # Params: # * verb, apply proper third person rules to this def present_third_person_singular_form_for(verb) infinitive = verb.is_a?(Verb) ? verb.infinitive.to_s : verb.to_s if infinitive =~ /[a-z&&#{CONSONANT_PATTERN}]y$/i infinitive[0..-2] + 'ies' elsif infinitive =~ /(ss|sh|t?ch|zz|x|#{CONSONANT_PATTERN}o)$/i infinitive + 'es' elsif infinitive =~ /[^s]s$/i infinitive + 'ses' else infinitive + 's' end end # Apply the regular past tense to a given verb for the conjugation # Params: # * verb, apply regular past tense rules to this def regular_preterite_for(verb) infinitive = verb.is_a?(Verb) ? verb.infinitive.to_s : verb.to_s if verb.to_s.match(/#{CONSONANT_PATTERN}#{VOWEL_PATTERN}#{DOUBLED_CONSONANT_PATTERN}$/) and !conjugations.single_terminal_consonants.include?(verb.to_sym) regular_preterite_with_doubled_terminal_consonant_for verb elsif verb.to_s.match(/#{CONSONANT_PATTERN}e$/) or verb.to_s.match(/ye$/) or verb.to_s.match(/oe$/) or verb.to_s.match(/nge$/) or verb.to_s.match(/ie$/) or verb.to_s.match(/ee$/) infinitive.to_s.concat('d').to_sym elsif verb.to_s.match(/#{CONSONANT_PATTERN}y$/) infinitive.to_s.chomp('y').concat('ied').to_sym else infinitive.to_s.concat('ed').to_sym end end # Apply proper rules to consonant endings # Params: # * verb, apply doule consonant to this def regular_preterite_with_doubled_terminal_consonant_for(verb) regular_preterite_for verb.to_s.concat(verb.to_s[-1,1]).to_sym end # Apply proper rules to consonant endings # Params: # * verb, apply doule consonant to this def present_participle_with_doubled_terminal_consonant_for(verb) present_participle verb.to_s.concat(verb.to_s[-1,1]).to_sym end # Add appropriate aspects to the tense of the conjugation # Params: # * tense, an option given by the user # * aspect, an option given by the user def form_for(tense, aspect) form = [] if tense == :future form << 'will' form << :infinitive if aspect == :habitual form.concat ['have', :past_participle] if aspect == :perfect form.concat ['be having', :past_participle] if aspect == :perfective form.concat ['be', :present_participle] if aspect == :progressive form.concat ['be about to', :infinitive] if aspect == :prospective else form.concat ['used to', :infinitive] if [tense, aspect] == [:past, :habitual] form.concat [:have, :past_participle] if aspect == :perfect form << :past if [tense, aspect] == [:past, :perfective] form.concat [:be, :present_participle] if aspect == :progressive form.concat [:be, 'about to', :infinitive] if aspect == :prospective form << :present if [tense, aspect] == [:present, :habitual] form.concat [:be, 'having', :past_participle] if [tense, aspect] == [:present, :perfective] end form end # Confirm an imperative mood contains the present tense and second person # Params: # * tense, an option given by the user # * person, how the conjugation refers to the subject # * mood, an option given by the user def check_for_improper_constructions(tense, person, mood) if mood == :imperative and not (person == :second and tense == :present) raise Verbs::ImproperConstruction, 'The imperative mood requires present tense and second person' end end end end