Class: Jido::Conjugator

Inherits:
Object
  • Object
show all
Defined in:
lib/jido/conjugator.rb

Overview

This is it, folks.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(lang, options = {}) ⇒ Conjugator

Create a Jido::Conjugator instance. Load and parse the corresponding XML data file, and parse any provided options.

Accepted options (keys of the ‘options` hash should be symbols, not strings):

  • :forms: Only return conjugations for the given verb forms / tenses.

    Jido::Conjugator.new 'fr', :forms => %w{prs futant}
    
  • :paradigms: Only return conjugations for the given paradigms.

    Jido::Conjugator.new 'fr', :paradigms => [{:person => '1', :quant => 'sg'}]
    
  • :forms_except: Return all conjugations except those for the given verb forms / tenses.

    Jido::Conjugator.new 'fr', :forms_except => 'prs'
    
  • :paradigms_except: Return all conjugations except those for the given paradigms.

    Jido::Conjugator.new 'fr', :paradigms_except => [{:person => '3', :quant => 'pl'}]
    


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/jido/conjugator.rb', line 26

def initialize lang, options = {}
  @lang = lang
  
  data_file_path = File.join(File.dirname(__FILE__), 'data', lang + '.xml')
  data_file = nil
  
  begin
    data_file = open data_file_path, 'r'
  rescue IOError
    raise "There was an error loading the data file for the given language."
  end
  
  @data = Nokogiri.XML data_file, nil, 'UTF-8'
  data_file.close

  self.options = options
end

Instance Attribute Details

#langObject (readonly)

The language used in this Conjugator instance. Verbs given to this instance are expected to be in this language. This instance will conjugate verbs according to rules of this language.



12
13
14
# File 'lib/jido/conjugator.rb', line 12

def lang
  @lang
end

Instance Method Details

#check_for_list_option(option_name) ⇒ Object

Interpret the provided option when a list is expected. Used to provide functionality like:

jido.conjugate 'be', :form => 'prs'
jido.conjugate 'be', :form => %w{prs pst prf}


58
59
60
61
62
63
64
65
# File 'lib/jido/conjugator.rb', line 58

def check_for_list_option option_name
  return nil if @options[option_name].nil?

  return [@options[option_name]] if @options[option_name].is_a?(String) or @options[option_name].is_a(Hash)
  return @options[option_name] if @options[option_name].is_a?(Array)

  raise "Invalid data type provided for option #{option_name}: a list was expected. Please provide a single string element or an array of strings."
end

#conjugate(verb) ⇒ Object

Hmm.. what does this do.. ?



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/jido/conjugator.rb', line 139

def conjugate verb
  @current_el = @data.at_xpath "/verbs/verb[@word='#{verb}']"
  @current_el = get_fallback_for_verb(verb) if @current_el.nil?
  return false if @current_el.nil?
  
  ret = {}
  @current_el_parents = [] # array of parents of the element, sorted by priority - parents earlier in the array will be picked over later ones
  store_parents @current_el # populate the parents array
  
  group = nil; group_search = nil
  forms.each do |form|
    next if @forms_except.include?(form)
    ret[form] = {}
    
    group_search = "group[@id='#{form}']"
    group = search_current_el group_search
    
    # grab modifier elements and extract their values
    group_prepend_el = group.at_xpath('prepend'); group_append_el = group.at_xpath('append'); group_mod_el = group.at_xpath('mod'); group_endlength_el = group.at_xpath('endlength')
    group_prepend = group_prepend_el.nil? ? nil : group_prepend_el.text
    group_append = group_append_el.nil? ? nil : group_append_el.text
    group_mod = group_mod_el.nil? ? nil : {:match => group_mod_el['match'], :search => group_mod_el['search'], :replace => group_mod_el['replace']}
    group_endlength = group_endlength_el.nil? ? nil : group_endlength_el.text.to_i
    
    pdgmgroup = nil; pdgmgroup_search = nil
    paradigm = nil; paradigm_search = nil
    paradigms.each do |paradigm|
      next if @paradigms_except.include?(paradigm)

      pdgmgroup_search = "group[@id='#{form}']/pdgmgroup[@id='#{paradigm[:person]}']"
      pdgmgroup = search_current_el pdgmgroup_search
      
      # skip this paradigm group if the "ignore" attribute is set
      next unless pdgmgroup['ignore'].nil?
      
      # grab modifier elements and extract their values
      # if unset, try to inherit from parent group
      pdgmgroup_prepend_el = pdgmgroup.at_xpath('prepend'); pdgmgroup_append_el = pdgmgroup.at_xpath('append'); pdgmgroup_mod_el = pdgmgroup.at_xpath('mod'); pdgmgroup_endlength_el = pdgmgroup.at_xpath('endlength')
      pdgmgroup_prepend = pdgmgroup_prepend_el.nil? ? group_prepend : pdgmgroup_prepend_el.text
      pdgmgroup_append = pdgmgroup_append_el.nil? ? group_append : pdgmgroup_append_el.text
      pdgmgroup_mod = pdgmgroup_mod_el.nil? ? group_mod : {:match => pdgmgroup_mod_el['match'], :search => pdgmgroup_mod_el['search'], :replace => pdgmgroup_mod_el['replace']}
      pdgmgroup_endlength = pdgmgroup_endlength_el.nil? ? group_endlength : pdgmgroup_endlength_el.text.to_i
      
      paradigm_search = "group[@id='#{form}']/pdgmgroup[@id='#{paradigm[:person]}']/paradigm[@id='#{paradigm[:quant]}']"
      paradigm_el = search_current_el paradigm_search
      
      # skip this paradigm if the "ignore" attribute is set
      next unless paradigm_el['ignore'].nil?
      
      # grab modifier elements and extract their values
      # if unset, try to inherit from parent paradigm group
      paradigm_prepend_el = paradigm_el.at_xpath('prepend'); paradigm_append_el = paradigm_el.at_xpath('append'); paradigm_mod_el = paradigm_el.at_xpath('mod'); paradigm_endlength_el = paradigm_el.at_xpath('endlength')
      prepend = paradigm_prepend_el.nil? ? pdgmgroup_prepend : paradigm_prepend_el.text
      append = paradigm_append_el.nil? ? pdgmgroup_append : paradigm_append_el.text
      mod = paradigm_mod_el.nil? ? pdgmgroup_mod : {:match => paradigm_mod_el['match'], :search => paradigm_mod_el['search'], :replace => paradigm_mod_el['replace']}
      
      endlength = paradigm_endlength_el.nil? ? pdgmgroup_endlength : paradigm_endlength_el.text.to_i
      endlength = 0 if endlength.nil? or endlength < 0
      
      # make a copy of verb to run the modifiers on
      modded_verb = verb
      
      # chop n chars from the end of the string, based on the <endlength> modifier
      modded_verb = modded_verb[0 ... ( modded_verb.length - endlength )] unless endlength.nil?
      
      # <mod> modifier (regex replacement)
      unless mod.nil?
        case mod[:match]
        when 'first' then modded_verb.sub!(mod[:search], mod[:replace])
        when 'all' then modded_verb.gsub!(mod[:search], mod[:replace])
        end
      end
      
      # <append> and <prepend> modifiers
      modded_verb = ( prepend.nil? ? '' : prepend ) + modded_verb + ( append.nil? ? '' : append )
      ret[form][paradigm[:person] + paradigm[:quant]] = modded_verb
    end
  end
  
  @current_el = nil
  @current_el_inheritor = nil
  
  ret
end

#fallbacksObject

Get the fallbacks for this language, in case an exact match for a given verb is not found.



97
98
99
100
101
102
103
104
105
106
# File 'lib/jido/conjugator.rb', line 97

def fallbacks
  if @fallbacks.nil?
    @fallbacks = []
    @data.xpath('/verbs/meta/fallbacks/fallback').each do |fallback|
      @fallbacks << {:regex => fallback['regex'], :ref => fallback['ref']}
    end
  end
  
  @fallbacks
end

#formsObject

Get the possible verb form IDs for any conjugated verb.

Jido.load('fr').forms # => ['PRS', 'PCOMP', 'IMP', ...]


69
70
71
72
73
74
75
76
77
78
# File 'lib/jido/conjugator.rb', line 69

def forms
  if @forms.nil?
    @forms = []
    @data.xpath('/verbs/meta/forms/form').each do |form|
      @forms << form.text
    end
  end
  
  @forms
end

#forms=(forms) ⇒ Object

Set the forms to conjugate. Use Conjugator#forms to find possible values for a certain instance.

jido.forms = %w{prs pst imp}  # conjugate only for present, past, and imperfect tenses
jido.forms = 'futant'         # conjugate only for future anterior tense
jido.forms = jido.forms[0..4] # conjugate for the first 5 forms stored in the conjugator


84
85
86
87
# File 'lib/jido/conjugator.rb', line 84

def forms= forms
  @options[:forms] = forms
  @forms = check_for_list_option :forms
end

#forms_except=(forms_except) ⇒ Object

Set the forms to not conjugate. Use Conjugator#forms to find possible values for a certain instance.

jido.forms_except = 'prs' # conjugate for all forms but the present tense


91
92
93
94
# File 'lib/jido/conjugator.rb', line 91

def forms_except= forms_except
  @options[:forms_except] = forms_except
  @forms_except = check_for_list_option :forms_except
end

#get_fallback_for_verb(verb) ⇒ Object

Find a fallback verbset whose regex matches the given verb. Used when an exact verb element cannot be matched with an input verb (that is, in most cases).



258
259
260
261
262
263
264
265
266
267
# File 'lib/jido/conjugator.rb', line 258

def get_fallback_for_verb verb
  fallbacks.each do |fallback|
    if verb.match fallback[:regex]
      ret = @data.at_xpath "/verbs/verbset[@id='#{fallback[:ref]}']"
      return ret
    end
  end
  
  false
end

#inspectObject

Return a string describing the instance.



270
271
272
# File 'lib/jido/conjugator.rb', line 270

def inspect
  "#<Conjugator @lang=\"#{@lang}\">"
end

#options=(options) ⇒ Object

Change the options for this Conjugator instance. See Conjugator::new for possible options.



46
47
48
49
50
51
52
# File 'lib/jido/conjugator.rb', line 46

def options= options
  @options = options
  @forms = check_for_list_option :forms
  @forms_except = check_for_list_option(:forms_except) || [ ]
  @paradigms = check_for_list_option :paradigms
  @paradigms_except = check_for_list_option(:paradigms_except) || [ ]
end

#paradigmsObject

Get the possible paradigm IDs for any conjugated verb. Each paradigm is a hash, with two keys:

  • :person

  • :quant

    Jido.load('fr').paradigms # => [{:person => '1', :quant => 'sg'}, {:person => '1', :quant => 'pl'}, ...]
    


114
115
116
117
118
119
120
121
122
123
# File 'lib/jido/conjugator.rb', line 114

def paradigms
  if @paradigms.nil?
    @paradigms = []
    @data.xpath('/verbs/meta/paradigms/paradigm').each do |paradigm|
      @paradigms << {:person => paradigm['person'], :quant => paradigm['quant']}
    end
  end
  
  @paradigms
end

#paradigms=(paradigms) ⇒ Object

See Conjugator#paradigms for the expected structure of the parameter.

jido.paradigms = [{:person => '1', :quant => 'sg'}, {:person => '3', :quant => 'pl'}] # conjugate for only 1SG and 3PL


127
128
129
130
# File 'lib/jido/conjugator.rb', line 127

def paradigms= paradigms
  @options[:paradigms] = paradigms
  @paradigms = check_for_list_option :paradigms
end

#paradigms_except=(paradigms_except) ⇒ Object

See Conjugator#paradigms for the expected structure of the parameter.



133
134
135
136
# File 'lib/jido/conjugator.rb', line 133

def paradigms_except= paradigms_except
  @options[:paradigms_except] = paradigms_except
  @paradigms_except = check_for_list_option :paradigms_except
end

#search_current_el(xpath) ⇒ Object

Search each parent of some verb for a given element. Used for rule inheritance.



242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/jido/conjugator.rb', line 242

def search_current_el xpath
  # try to find the rule in the current verb
  desired_el = @current_el.at_xpath xpath
  return desired_el unless desired_el.nil?
  
  # check all the verb's parents, walking up the hierarchy
  @current_el_parents.each do |parent|
    desired_el = parent.at_xpath xpath
    return desired_el unless desired_el.nil?
  end
  
  nil
end

#store_parents(el) ⇒ Object

Find all parents of a given verb / verbset, and store them in @current_el_parents (if a verb / verbset #1 inherits a verb / verbset #2, then #2 is the parent of #1)

Structure note: verbs are final objects. Verbs cannot be inherited; they are always at the bottom of a hierarchy. Verbsets can inherit / be inherited by other verbsets, and verbs can inherit verbsets. In other words.. <strong>verb = final class, verb set = abstract class</strong>.

Yay, recursion :)



232
233
234
235
236
237
238
# File 'lib/jido/conjugator.rb', line 232

def store_parents el
  return if el['inherit'].nil?
  
  inherited_el = @data.at_xpath "/verbs/verbset[@id='#{el['inherit']}']"
  @current_el_parents << inherited_el
  store_parents inherited_el
end