romanian.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. from .bases import _StandardStemmer
  2. from whoosh.compat import u
  3. class RomanianStemmer(_StandardStemmer):
  4. """
  5. The Romanian Snowball stemmer.
  6. :cvar __vowels: The Romanian vowels.
  7. :type __vowels: unicode
  8. :cvar __step0_suffixes: Suffixes to be deleted in step 0 of the algorithm.
  9. :type __step0_suffixes: tuple
  10. :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm.
  11. :type __step1_suffixes: tuple
  12. :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm.
  13. :type __step2_suffixes: tuple
  14. :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm.
  15. :type __step3_suffixes: tuple
  16. :note: A detailed description of the Romanian
  17. stemming algorithm can be found under
  18. http://snowball.tartarus.org/algorithms/romanian/stemmer.html
  19. """
  20. __vowels = u("aeiou\u0103\xE2\xEE")
  21. __step0_suffixes = ('iilor', 'ului', 'elor', 'iile', 'ilor',
  22. 'atei', u('a\u0163ie'), u('a\u0163ia'), 'aua',
  23. 'ele', 'iua', 'iei', 'ile', 'ul', 'ea',
  24. 'ii')
  25. __step1_suffixes = ('abilitate', 'abilitati', u('abilit\u0103\u0163i'),
  26. 'ibilitate', u('abilit\u0103i'), 'ivitate',
  27. 'ivitati', u('ivit\u0103\u0163i'), 'icitate',
  28. 'icitati', u('icit\u0103\u0163i'), 'icatori',
  29. u('ivit\u0103i'), u('icit\u0103i'), 'icator',
  30. u('a\u0163iune'), 'atoare', u('\u0103toare'),
  31. u('i\u0163iune'), 'itoare', 'iciva', 'icive',
  32. 'icivi', u('iciv\u0103'), 'icala', 'icale',
  33. 'icali', u('ical\u0103'), 'ativa', 'ative',
  34. 'ativi', u('ativ\u0103'), 'atori', u('\u0103tori'),
  35. 'itiva', 'itive', 'itivi', u('itiv\u0103'),
  36. 'itori', 'iciv', 'ical', 'ativ', 'ator',
  37. u('\u0103tor'), 'itiv', 'itor')
  38. __step2_suffixes = ('abila', 'abile', 'abili', u('abil\u0103'),
  39. 'ibila', 'ibile', 'ibili', u('ibil\u0103'),
  40. 'atori', 'itate', 'itati', u('it\u0103\u0163i'),
  41. 'abil', 'ibil', 'oasa', u('oas\u0103'), 'oase',
  42. 'anta', 'ante', 'anti', u('ant\u0103'), 'ator',
  43. u('it\u0103i'), 'iune', 'iuni', 'isme', 'ista',
  44. 'iste', 'isti', u('ist\u0103'), u('i\u015Fti'),
  45. 'ata', u('at\u0103'), 'ati', 'ate', 'uta',
  46. u('ut\u0103'), 'uti', 'ute', 'ita', u('it\u0103'),
  47. 'iti', 'ite', 'ica', 'ice', 'ici', u('ic\u0103'),
  48. 'osi', u('o\u015Fi'), 'ant', 'iva', 'ive', 'ivi',
  49. u('iv\u0103'), 'ism', 'ist', 'at', 'ut', 'it',
  50. 'ic', 'os', 'iv')
  51. __step3_suffixes = (u('seser\u0103\u0163i'), u('aser\u0103\u0163i'),
  52. u('iser\u0103\u0163i'), u('\xE2ser\u0103\u0163i'),
  53. u('user\u0103\u0163i'), u('seser\u0103m'),
  54. u('aser\u0103m'), u('iser\u0103m'), u('\xE2ser\u0103m'),
  55. u('user\u0103m'), u('ser\u0103\u0163i'), u('sese\u015Fi'),
  56. u('seser\u0103'), u('easc\u0103'), u('ar\u0103\u0163i'),
  57. u('ur\u0103\u0163i'), u('ir\u0103\u0163i'),
  58. u('\xE2r\u0103\u0163i'), u('ase\u015Fi'),
  59. u('aser\u0103'), u('ise\u015Fi'), u('iser\u0103'),
  60. u('\xe2se\u015Fi'), u('\xE2ser\u0103'),
  61. u('use\u015Fi'), u('user\u0103'), u('ser\u0103m'),
  62. 'sesem', 'indu', '\xE2ndu', u('eaz\u0103'),
  63. u('e\u015Fti'), u('e\u015Fte'), u('\u0103\u015Fti'),
  64. u('\u0103\u015Fte'), u('ea\u0163i'), u('ia\u0163i'),
  65. u('ar\u0103m'), u('ur\u0103m'), u('ir\u0103m'),
  66. u('\xE2r\u0103m'), 'asem', 'isem',
  67. '\xE2sem', 'usem', u('se\u015Fi'), u('ser\u0103'),
  68. 'sese', 'are', 'ere', 'ire', '\xE2re',
  69. 'ind', '\xE2nd', 'eze', 'ezi', 'esc',
  70. u('\u0103sc'), 'eam', 'eai', 'eau', 'iam',
  71. 'iai', 'iau', u('a\u015Fi'), u('ar\u0103'),
  72. u('u\u015Fi'), u('ur\u0103'), u('i\u015Fi'), u('ir\u0103'),
  73. u('\xE2\u015Fi'), u('\xe2r\u0103'), 'ase',
  74. 'ise', '\xE2se', 'use', u('a\u0163i'),
  75. u('e\u0163i'), u('i\u0163i'), u('\xe2\u0163i'), 'sei',
  76. 'ez', 'am', 'ai', 'au', 'ea', 'ia', 'ui',
  77. '\xE2i', u('\u0103m'), 'em', 'im', '\xE2m',
  78. 'se')
  79. def stem(self, word):
  80. """
  81. Stem a Romanian word and return the stemmed form.
  82. :param word: The word that is stemmed.
  83. :type word: str or unicode
  84. :return: The stemmed form.
  85. :rtype: unicode
  86. """
  87. word = word.lower()
  88. step1_success = False
  89. step2_success = False
  90. for i in range(1, len(word) - 1):
  91. if word[i - 1] in self.__vowels and word[i + 1] in self.__vowels:
  92. if word[i] == "u":
  93. word = "".join((word[:i], "U", word[i + 1:]))
  94. elif word[i] == "i":
  95. word = "".join((word[:i], "I", word[i + 1:]))
  96. r1, r2 = self._r1r2_standard(word, self.__vowels)
  97. rv = self._rv_standard(word, self.__vowels)
  98. # STEP 0: Removal of plurals and other simplifications
  99. for suffix in self.__step0_suffixes:
  100. if word.endswith(suffix):
  101. if suffix in r1:
  102. if suffix in ("ul", "ului"):
  103. word = word[:-len(suffix)]
  104. if suffix in rv:
  105. rv = rv[:-len(suffix)]
  106. else:
  107. rv = ""
  108. elif (suffix == "aua" or suffix == "atei" or
  109. (suffix == "ile" and word[-5:-3] != "ab")):
  110. word = word[:-2]
  111. elif suffix in ("ea", "ele", "elor"):
  112. word = "".join((word[:-len(suffix)], "e"))
  113. if suffix in rv:
  114. rv = "".join((rv[:-len(suffix)], "e"))
  115. else:
  116. rv = ""
  117. elif suffix in ("ii", "iua", "iei",
  118. "iile", "iilor", "ilor"):
  119. word = "".join((word[:-len(suffix)], "i"))
  120. if suffix in rv:
  121. rv = "".join((rv[:-len(suffix)], "i"))
  122. else:
  123. rv = ""
  124. elif suffix in ("a\u0163ie", "a\u0163ia"):
  125. word = word[:-1]
  126. break
  127. # STEP 1: Reduction of combining suffixes
  128. while True:
  129. replacement_done = False
  130. for suffix in self.__step1_suffixes:
  131. if word.endswith(suffix):
  132. if suffix in r1:
  133. step1_success = True
  134. replacement_done = True
  135. if suffix in ("abilitate", "abilitati",
  136. "abilit\u0103i",
  137. "abilit\u0103\u0163i"):
  138. word = "".join((word[:-len(suffix)], "abil"))
  139. elif suffix == "ibilitate":
  140. word = word[:-5]
  141. elif suffix in ("ivitate", "ivitati",
  142. "ivit\u0103i",
  143. "ivit\u0103\u0163i"):
  144. word = "".join((word[:-len(suffix)], "iv"))
  145. elif suffix in ("icitate", "icitati", "icit\u0103i",
  146. "icit\u0103\u0163i", "icator",
  147. "icatori", "iciv", "iciva",
  148. "icive", "icivi", "iciv\u0103",
  149. "ical", "icala", "icale", "icali",
  150. "ical\u0103"):
  151. word = "".join((word[:-len(suffix)], "ic"))
  152. elif suffix in ("ativ", "ativa", "ative", "ativi",
  153. "ativ\u0103", "a\u0163iune",
  154. "atoare", "ator", "atori",
  155. "\u0103toare",
  156. "\u0103tor", "\u0103tori"):
  157. word = "".join((word[:-len(suffix)], "at"))
  158. if suffix in r2:
  159. r2 = "".join((r2[:-len(suffix)], "at"))
  160. elif suffix in ("itiv", "itiva", "itive", "itivi",
  161. "itiv\u0103", "i\u0163iune",
  162. "itoare", "itor", "itori"):
  163. word = "".join((word[:-len(suffix)], "it"))
  164. if suffix in r2:
  165. r2 = "".join((r2[:-len(suffix)], "it"))
  166. else:
  167. step1_success = False
  168. break
  169. if not replacement_done:
  170. break
  171. # STEP 2: Removal of standard suffixes
  172. for suffix in self.__step2_suffixes:
  173. if word.endswith(suffix):
  174. if suffix in r2:
  175. step2_success = True
  176. if suffix in ("iune", "iuni"):
  177. if word[-5] == "\u0163":
  178. word = "".join((word[:-5], "t"))
  179. elif suffix in ("ism", "isme", "ist", "ista", "iste",
  180. "isti", "ist\u0103", "i\u015Fti"):
  181. word = "".join((word[:-len(suffix)], "ist"))
  182. else:
  183. word = word[:-len(suffix)]
  184. break
  185. # STEP 3: Removal of verb suffixes
  186. if not step1_success and not step2_success:
  187. for suffix in self.__step3_suffixes:
  188. try:
  189. if word.endswith(suffix):
  190. if suffix in rv:
  191. if suffix in (u('seser\u0103\u0163i'), u('seser\u0103m'),
  192. u('ser\u0103\u0163i'), u('sese\u015Fi'),
  193. u('seser\u0103'), u('ser\u0103m'), 'sesem',
  194. u('se\u015Fi'), u('ser\u0103'), 'sese',
  195. u('a\u0163i'), u('e\u0163i'), u('i\u0163i'),
  196. u('\xE2\u0163i'), 'sei', u('\u0103m'),
  197. 'em', 'im', '\xE2m', 'se'):
  198. word = word[:-len(suffix)]
  199. rv = rv[:-len(suffix)]
  200. else:
  201. if (not rv.startswith(suffix) and
  202. rv[rv.index(suffix) - 1] not in
  203. "aeio\u0103\xE2\xEE"):
  204. word = word[:-len(suffix)]
  205. break
  206. except UnicodeDecodeError:
  207. # The word is unicode, but suffix is not
  208. continue
  209. # STEP 4: Removal of final vowel
  210. for suffix in ("ie", "a", "e", "i", "\u0103"):
  211. if word.endswith(suffix):
  212. if suffix in rv:
  213. word = word[:-len(suffix)]
  214. break
  215. word = word.replace("I", "i").replace("U", "u")
  216. return word