lingo 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. data/.rspec +1 -0
  2. data/COPYING +663 -0
  3. data/ChangeLog +754 -0
  4. data/README +322 -0
  5. data/Rakefile +100 -0
  6. data/TODO +28 -0
  7. data/bin/lingo +5 -0
  8. data/bin/lingoctl +6 -0
  9. data/de.lang +121 -0
  10. data/de/lingo-abk.txt +74 -0
  11. data/de/lingo-dic.txt +56822 -0
  12. data/de/lingo-mul.txt +3209 -0
  13. data/de/lingo-syn.txt +14841 -0
  14. data/de/test_dic.txt +24 -0
  15. data/de/test_mul.txt +17 -0
  16. data/de/test_mul2.txt +2 -0
  17. data/de/test_singleword.txt +2 -0
  18. data/de/test_syn.txt +4 -0
  19. data/de/test_syn2.txt +1 -0
  20. data/de/user-dic.txt +10 -0
  21. data/en.lang +113 -0
  22. data/en/lingo-dic.txt +55434 -0
  23. data/en/lingo-mul.txt +456 -0
  24. data/en/user-dic.txt +5 -0
  25. data/info/Objekte.png +0 -0
  26. data/info/Typen.png +0 -0
  27. data/info/database.png +0 -0
  28. data/info/db_small.png +0 -0
  29. data/info/download.png +0 -0
  30. data/info/gpl-hdr.txt +27 -0
  31. data/info/kerze.png +0 -0
  32. data/info/language.png +0 -0
  33. data/info/lingo.png +0 -0
  34. data/info/logo.png +0 -0
  35. data/info/meeting.png +0 -0
  36. data/info/types.png +0 -0
  37. data/lib/lingo.rb +321 -0
  38. data/lib/lingo/attendee/abbreviator.rb +119 -0
  39. data/lib/lingo/attendee/debugger.rb +111 -0
  40. data/lib/lingo/attendee/decomposer.rb +101 -0
  41. data/lib/lingo/attendee/dehyphenizer.rb +167 -0
  42. data/lib/lingo/attendee/multiworder.rb +301 -0
  43. data/lib/lingo/attendee/noneword_filter.rb +103 -0
  44. data/lib/lingo/attendee/objectfilter.rb +86 -0
  45. data/lib/lingo/attendee/sequencer.rb +190 -0
  46. data/lib/lingo/attendee/synonymer.rb +105 -0
  47. data/lib/lingo/attendee/textreader.rb +237 -0
  48. data/lib/lingo/attendee/textwriter.rb +196 -0
  49. data/lib/lingo/attendee/tokenizer.rb +218 -0
  50. data/lib/lingo/attendee/variator.rb +185 -0
  51. data/lib/lingo/attendee/vector_filter.rb +158 -0
  52. data/lib/lingo/attendee/wordsearcher.rb +96 -0
  53. data/lib/lingo/attendees.rb +289 -0
  54. data/lib/lingo/cli.rb +62 -0
  55. data/lib/lingo/config.rb +104 -0
  56. data/lib/lingo/const.rb +131 -0
  57. data/lib/lingo/ctl.rb +173 -0
  58. data/lib/lingo/database.rb +587 -0
  59. data/lib/lingo/language.rb +530 -0
  60. data/lib/lingo/modules.rb +98 -0
  61. data/lib/lingo/types.rb +285 -0
  62. data/lib/lingo/utilities.rb +40 -0
  63. data/lib/lingo/version.rb +27 -0
  64. data/lingo-all.cfg +85 -0
  65. data/lingo-call.cfg +15 -0
  66. data/lingo.cfg +78 -0
  67. data/lingo.rb +3 -0
  68. data/lir.cfg +72 -0
  69. data/porter/stem.cfg +311 -0
  70. data/porter/stem.rb +150 -0
  71. data/spec/spec_helper.rb +0 -0
  72. data/test.cfg +79 -0
  73. data/test/attendee/ts_abbreviator.rb +35 -0
  74. data/test/attendee/ts_decomposer.rb +31 -0
  75. data/test/attendee/ts_multiworder.rb +390 -0
  76. data/test/attendee/ts_noneword_filter.rb +19 -0
  77. data/test/attendee/ts_objectfilter.rb +19 -0
  78. data/test/attendee/ts_sequencer.rb +43 -0
  79. data/test/attendee/ts_synonymer.rb +33 -0
  80. data/test/attendee/ts_textreader.rb +58 -0
  81. data/test/attendee/ts_textwriter.rb +98 -0
  82. data/test/attendee/ts_tokenizer.rb +32 -0
  83. data/test/attendee/ts_variator.rb +24 -0
  84. data/test/attendee/ts_vector_filter.rb +62 -0
  85. data/test/attendee/ts_wordsearcher.rb +119 -0
  86. data/test/lir.csv +3 -0
  87. data/test/lir.txt +12 -0
  88. data/test/lir2.txt +12 -0
  89. data/test/mul.txt +1 -0
  90. data/test/ref/artikel.mul +1 -0
  91. data/test/ref/artikel.non +159 -0
  92. data/test/ref/artikel.seq +270 -0
  93. data/test/ref/artikel.syn +16 -0
  94. data/test/ref/artikel.vec +928 -0
  95. data/test/ref/artikel.ven +928 -0
  96. data/test/ref/artikel.ver +928 -0
  97. data/test/ref/lir.csv +328 -0
  98. data/test/ref/lir.mul +1 -0
  99. data/test/ref/lir.non +274 -0
  100. data/test/ref/lir.seq +249 -0
  101. data/test/ref/lir.syn +94 -0
  102. data/test/test_helper.rb +113 -0
  103. data/test/ts_database.rb +269 -0
  104. data/test/ts_language.rb +396 -0
  105. data/txt/artikel-en.txt +157 -0
  106. data/txt/artikel.txt +170 -0
  107. data/txt/lir.txt +1317 -0
  108. metadata +211 -0
@@ -0,0 +1,530 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # LINGO ist ein Indexierungssystem mit Grundformreduktion, Kompositumzerlegung,
5
+ # Mehrworterkennung und Relationierung.
6
+ #
7
+ # Copyright (C) 2005-2007 John Vorhauer
8
+ # Copyright (C) 2007-2011 John Vorhauer, Jens Wille
9
+ #
10
+ # This program is free software; you can redistribute it and/or modify it under
11
+ # the terms of the GNU Affero General Public License as published by the Free
12
+ # Software Foundation; either version 3 of the License, or (at your option)
13
+ # any later version.
14
+ #
15
+ # This program is distributed in the hope that it will be useful, but WITHOUT
16
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
18
+ # details.
19
+ #
20
+ # You should have received a copy of the GNU Affero General Public License along
21
+ # with this program; if not, write to the Free Software Foundation, Inc.,
22
+ # 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
23
+ #
24
+ # For more information visit http://www.lex-lingo.de or contact me at
25
+ # welcomeATlex-lingoDOTde near 50°55'N+6°55'E.
26
+ #
27
+ # Lex Lingo rules from here on
28
+ #++
29
+
30
+ require_relative 'const'
31
+ require_relative 'modules'
32
+ require_relative 'database'
33
+
34
+ class Lingo
35
+
36
+ # Die Klasse LexicalHash ermöglicht den Zugriff auf die Lingodatenbanken. Im Gegensatz zur
37
+ # Klasse DbmFile, welche nur Strings als Ergebnis zurück gibt, wird hier als Ergebnis ein
38
+ # Array von Lexical-Objekten zurück gegeben.
39
+
40
+ class LexicalHash
41
+
42
+ include Cachable
43
+ include Reportable
44
+
45
+ def initialize(id, lingo)
46
+ init_reportable
47
+ init_cachable
48
+ report_prefix( id )
49
+
50
+ # Parameter aus de.lang:language/dictionary/databases auslesen
51
+ config = lingo.config['language/dictionary/databases/' + id]
52
+ Lingo.error( "LexicalHash kann Datenquelle mit ID '#{id}' in de.lang:language/dictionary/databases' nicht finden" ) if config.nil?
53
+
54
+ @wordclass = config.fetch( 'def-wc', LA_UNKNOWN )
55
+
56
+ # Store erzeugen
57
+ @source = DbmFile.new(id, lingo)
58
+ @source.open
59
+ end
60
+
61
+ def close
62
+ @source.close
63
+ end
64
+
65
+ def [](ikey)
66
+ # Schlüssel normalisieren
67
+ inc('total requests')
68
+ key = ikey.downcase
69
+
70
+ # Cache abfragen
71
+ if hit?(key)
72
+ inc('cache hits')
73
+ return retrieve(key)
74
+ end
75
+
76
+ # Wert aus Datenbank lesen
77
+ inc('source reads')
78
+ record = @source[key]
79
+
80
+ # Werte in interne Objekte umwandeln
81
+ record = record.collect do |str|
82
+ case str
83
+ when /^\*\d+$/
84
+ # Hinweis für Multiworder, dass Multiword mit (\d) Wörtern länge zu prüfen ist
85
+ str
86
+ when /^#(.)$/
87
+ # Alleinige Angabe der Wortklasse => Ergebniswort ist gleich dem Schlüssel
88
+ Lexical.new(key, $1)
89
+ when /^([^#]+?)\s*#(.)$/
90
+ # Angabe Ergebniswort und Wortklasse
91
+ Lexical.new($1, $2)
92
+ when /^([^#]+)$/
93
+ # Angabe Ergebniswort ohne Wortklasse
94
+ Lexical.new($1, @wordclass)
95
+ else
96
+ str
97
+ end
98
+ end.compact.sort.uniq unless record.nil?
99
+
100
+ # Ergebnis zurückgeben
101
+ inc('data found') unless record.nil?
102
+ store(key, record)
103
+ end
104
+
105
+ end
106
+
107
+ class Dictionary
108
+
109
+ include Cachable
110
+ include Reportable
111
+
112
+ def initialize(config, lingo)
113
+ init_reportable
114
+ init_cachable
115
+
116
+ dictionary_config = lingo.dictionary_config
117
+
118
+ # Parameter prüfen
119
+ raise "Keine Sprach-Konfiguration angegeben!" if dictionary_config.nil?
120
+ raise "Keine Parameter angegeben!" if config.nil?
121
+ raise "Keine Datenquellen angegeben!" unless config.has_key?('source')
122
+
123
+ # Parameter auslesen
124
+ @all_sources = (config['mode'].nil? || config['mode'].downcase=='all')
125
+
126
+ @sources = config['source'].map { |src| LexicalHash.new(src, lingo) }
127
+
128
+ lingo.dictionaries << self
129
+
130
+ # Parameter aus de.lang:language/dictionary auslesen
131
+ @suffixes = []
132
+ @infixes = []
133
+
134
+ dictionary_config['suffix'].each {|arr|
135
+ typ, sufli = arr
136
+ typ.downcase!
137
+ sufli.split.each {|suf|
138
+ su, ex = suf.split('/')
139
+ fix = [Regexp.new(su+'$', 'i'), ex.nil? ? '*' : ex, typ]
140
+ (typ=='f' ? @infixes : @suffixes) << fix
141
+ }
142
+ } if dictionary_config.has_key?( 'suffix' )
143
+ end
144
+
145
+ def close
146
+ @sources.each(&:close)
147
+ end
148
+
149
+ def report
150
+ super.tap { |rep| @sources.each { |src| rep.update(src.report) } }
151
+ end
152
+
153
+ # _dic_.find_word( _aString_ ) -> _aNewWord_
154
+ #
155
+ # Erstellt aus dem String ein Wort und sucht nach diesem im Wörterbuch.
156
+ def find_word(string)
157
+ # Cache abfragen
158
+ key = string.downcase
159
+ if hit?(key)
160
+ inc('cache hits')
161
+ word = retrieve(key)
162
+ word.form = string
163
+ return word
164
+ end
165
+
166
+ word = Word.new(string, WA_UNKNOWN)
167
+ lexicals = select_with_suffix(string)
168
+ unless lexicals.empty?
169
+ word.lexicals = lexicals
170
+ word.attr = WA_IDENTIFIED
171
+ end
172
+ store(key, word)
173
+ end
174
+
175
+ def find_synonyms(obj)
176
+ # alle Lexicals des Wortes
177
+ lexis = obj.lexicals
178
+ lexis = [obj] if lexis.empty? && obj.attr==WA_UNKNOWN
179
+ # alle gefundenen Synonyme
180
+ synos = []
181
+ # multiworder optimization
182
+ key_ref = %r{\A#{Regexp.escape(KEY_REF)}\d+}o
183
+
184
+ lexis.each do |lex|
185
+ # Synonyme für Teile eines Kompositum ausschließen
186
+ next if obj.attr==WA_KOMPOSITUM && lex.attr!=LA_KOMPOSITUM
187
+ # Synonyme für Synonyme ausschließen
188
+ next if lex.attr==LA_SYNONYM
189
+
190
+ select(lex.form).each do |syn|
191
+ synos << syn unless syn =~ key_ref
192
+ end
193
+ end
194
+
195
+ synos
196
+ end
197
+
198
+ # _dic_.select( _aString_ ) -> _ArrayOfLexicals_
199
+ #
200
+ # Sucht alle Wörterbücher durch und gibt den ersten Treffer zurück (+mode = first+), oder alle Treffer (+mode = all+)
201
+ def select(string)
202
+ lexicals = []
203
+
204
+ @sources.each { |src|
205
+ if lexis = src[string]
206
+ lexicals += lexis
207
+ break unless @all_sources
208
+ end
209
+ }
210
+
211
+ lexicals.sort.uniq
212
+ end
213
+
214
+ # _dic_.select_with_suffix( _aString_ ) -> _ArrayOfLexicals_
215
+ #
216
+ # Sucht alle Wörterbücher durch und gibt den ersten Treffer zurück (+mode = first+), oder alle Treffer (+mode = all+).
217
+ # Sucht dabei auch Wörter, die um wortklassenspezifische Suffixe bereinigt wurden.
218
+ def select_with_suffix(string)
219
+ lexicals = select(string)
220
+ if lexicals.empty?
221
+ suffix_lexicals(string).each { |suflex|
222
+ select(suflex.form).each { |srclex|
223
+ lexicals << srclex if suflex.attr == srclex.attr
224
+ }
225
+ }
226
+ end
227
+ lexicals
228
+ end
229
+
230
+ # _dic_.select_with_infix( _aString_ ) -> _ArrayOfLexicals_
231
+ #
232
+ # Sucht alle Wörterbücher durch und gibt den ersten Treffer zurück (+mode = first+), oder alle Treffer (+mode = all+).
233
+ # Sucht dabei auch Wörter, die eine Fugung am Ende haben.
234
+ def select_with_infix(string)
235
+ lexicals = select(string)
236
+ if lexicals.size == 0
237
+ infix_lexicals(string).each { |inlex|
238
+ select(inlex.form).each { |srclex|
239
+ lexicals << srclex
240
+ }
241
+ }
242
+ end
243
+ lexicals
244
+ end
245
+
246
+ # _dic_.suffix_lexicals( _aString_ ) -> _ArrayOfLexicals_
247
+ #
248
+ # Gibt alle möglichen Lexicals zurück, die von der Endung her auf den String anwendbar sind:
249
+ #
250
+ # dic.suffix_lexicals("Hasens") -> [(hasen/s), (hasen/e), (has/e)]
251
+ def suffix_lexicals(string)
252
+ lexicals = []
253
+ newform = regex = ext = type = nil
254
+ @suffixes.each { |suf|
255
+ regex, ext, type = suf
256
+ if string =~ regex
257
+ newform = $`+((ext=="*")?'':ext)+$'
258
+ lexicals << Lexical.new(newform, type)
259
+ end
260
+ }
261
+ lexicals
262
+ end
263
+
264
+ # _dic_.gap_lexicals( _aString_ ) -> _ArrayOfLexicals_
265
+ #
266
+ # Gibt alle möglichen Lexicals zurück, die von der Endung her auf den String anwendbar sind:
267
+ def infix_lexicals(string)
268
+ lexicals = []
269
+ newform = regex = ext = type = nil
270
+ @infixes.each { |suf|
271
+ regex, ext, type = suf
272
+ if string =~ regex
273
+ newform = $`+((ext=="*")?'':ext)+$'
274
+ lexicals << Lexical.new(newform, type)
275
+ end
276
+ }
277
+ lexicals
278
+ end
279
+
280
+ end
281
+
282
+ class Compositum
283
+ end
284
+
285
+ # Die Klasse Grammar beinhaltet grammatikalische Spezialitäten einer Sprache. Derzeit findet die
286
+ # Kompositumerkennung hier ihren Platz, die mit der Methode find_compositum aufgerufen werden kann.
287
+ # Die Klasse Grammar wird genau wie ein Dictionary initialisiert. Das bei der Initialisierung angegebene Wörterbuch ist Grundlage
288
+ # für die Erkennung der Kompositumteile.
289
+
290
+ class Grammar
291
+
292
+ # Ergebnisse der Kompositumerkennung werden gespeichert und bei erneutem Aufruf mit gleichem Suchwort genutzt
293
+ include Cachable
294
+
295
+ # Die Verarbeitung wird statistisch erfasst und mit der Option -s angezeigt
296
+ include Reportable
297
+
298
+ # initialize(config, dictionary_config) -> _Grammar_
299
+ # config = Attendee-spezifische Parameter
300
+ # dictionary_config = Datenbankkonfiguration aus de.lang
301
+ def initialize(config, lingo)
302
+ init_reportable
303
+ init_cachable
304
+
305
+ @dictionary = Dictionary.new(config, lingo)
306
+
307
+ # Sprachspezifische Einstellungen für Kompositumverarbeitung laden (die nachfolgenden Werte können in der
308
+ # Konfigurationsdatei de.lang nach belieben angepasst werden)
309
+ comp = lingo.dictionary_config['compositum']
310
+
311
+ # Ein Wort muss mindestens 8 Zeichen lang sein, damit überhaupt eine Prüfung stattfindet.
312
+ @comp_min_word_size = (comp['min-word-size'] || '8').to_i
313
+
314
+ # Die durchschnittliche Länge der Kompositum-Wortteile muss mindestens 4 Zeichen lang sein, sonst ist es kein
315
+ # gültiges Kompositum.
316
+ @comp_min_avg_part_size = (comp['min-avg-part-size'] || '4').to_i
317
+
318
+ # Der kürzeste Kompositum-Wortteil muss mindestens 1 Zeichen lang sein
319
+ @comp_min_part_size = (comp['min-part-size'] || '1').to_i
320
+
321
+ # Ein Kompositum darf aus höchstens 4 Wortteilen bestehen
322
+ @comp_max_parts = (comp['max-parts'] || '4').to_i
323
+
324
+ # Die Wortklasse eines Kompositum-Wortteils kann separat gekennzeichnet werden, um sie von Wortklassen normaler Wörter
325
+ # unterscheiden zu können z.B. Hausmeister => ['haus/s', 'meister/s'] oder Hausmeister => ['haus/s+', 'meister/s+'] mit
326
+ # append-wordclass = '+'
327
+ @append_wc = comp.fetch( 'append-wordclass', '' )
328
+
329
+ # Bestimmte Sequenzen können als ungültige Komposita erkannt werden, z.B. ist ein Kompositum aus zwei Adjetiven kein
330
+ # Kompositum, also skip-sequence = 'aa'
331
+ @sequences = comp.fetch( 'skip-sequences', [] ).collect { |sq| sq.downcase }
332
+
333
+ # Liste der Vorschläge für eine Zerlegung
334
+ @suggestions = []
335
+ end
336
+
337
+ def close
338
+ @dictionary.close
339
+ end
340
+
341
+ alias_method :report_grammar, :report
342
+
343
+ def report
344
+ rep = report_grammar
345
+ rep.update(@dictionary.report)
346
+ rep
347
+ end
348
+
349
+ # find_compositum(string) -> word wenn level=1
350
+ # find_compositum(string) -> [lexicals, stats] wenn level!=1
351
+ #
352
+ # find_compositum arbeitet in verschiedenen Leveln, da die Methode auch rekursiv aufgerufen wird. Ein Level größer 1
353
+ # entspricht daher einem rekursiven Aufruf
354
+ def find_compositum(string, level=1, has_tail=false)
355
+ # Prüfen, ob string bereits auf Kompositum getestet wurde. Wenn ja, dann Ergebnis des letztes Aufrufs zurück geben.
356
+ key = string.downcase
357
+ if level == 1 && hit?(key)
358
+ inc('cache hits')
359
+ return retrieve(key)
360
+ end
361
+
362
+ # Ergebnis vorbelegen
363
+ comp = Word.new(string, WA_UNKNOWN)
364
+
365
+ # Validitätsprüfung: nur Strings mit Mindestlänge auf Kompositum prüfen
366
+ if string.size <= @comp_min_word_size
367
+ inc('String zu kurz')
368
+ return (level==1) ? comp : [[],[],'']
369
+ end
370
+
371
+ # Kompositumerkennung initialisieren
372
+ inc('Komposita geprüft')
373
+ stats, lexis, seqs = permute_compositum(string.downcase, level, has_tail)
374
+
375
+ if level==1
376
+ # Auf Level 1 Kompositum zurück geben
377
+ if lexis.size > 0 && is_valid?( string, stats, lexis, seqs )
378
+ inc('Komposita erkannt')
379
+ comp.attr = WA_KOMPOSITUM
380
+ comp.lexicals = lexis.collect do |lex|
381
+ (lex.attr==LA_KOMPOSITUM) ? lex : Lexical.new(lex.form, lex.attr+@append_wc)
382
+ end
383
+ end
384
+
385
+ return store(key, comp)
386
+ end
387
+
388
+ # Validitätsprüfung
389
+ if lexis.size > 0 && is_valid?(string, stats, lexis, seqs)
390
+ [stats, lexis, seqs]
391
+ else
392
+ [[],[],'']
393
+ end
394
+ end
395
+
396
+ private
397
+
398
+ def is_valid?(string, stats, lexis, seqs)
399
+ is_valid = true
400
+ is_valid &&= (stats.size <= @comp_max_parts)
401
+ is_valid &&= (stats.sort[0] >= @comp_min_part_size)
402
+ is_valid &&= (string.size/stats.size) >= @comp_min_avg_part_size
403
+ is_valid &&= @sequences.index( seqs ).nil? unless @sequences.empty?
404
+ is_valid
405
+ end
406
+
407
+ # permute_string( _aString_ ) -> [lexicals, stats, seqs]
408
+ def permute_compositum(string, level, has_tail)
409
+ @suggestions[level] = [] if @suggestions[level].nil?
410
+
411
+ # Finde letzten Bindesstrich im Wort
412
+ if string =~ /^(.+)-([^-]+)$/
413
+ test_compositum($1, '-', $2, level, has_tail)
414
+ else
415
+ length = string.length
416
+
417
+ # Wortteilungen testen
418
+ 1.upto(length - 1) do |p|
419
+ # String teilen und testen
420
+ fr_str, ba_str = string.slice(0...p), string.slice(p...length)
421
+ stats, lexis, seqs = test_compositum(fr_str, '', ba_str, level, has_tail)
422
+
423
+ unless lexis.empty?
424
+ if lexis[-1].attr==LA_TAKEITASIS
425
+ # => halbes Kompositum
426
+ @suggestions[level] << [stats, lexis, seqs]
427
+ else
428
+ # => ganzes Kompositum
429
+ return [stats, lexis, seqs]
430
+ end
431
+ end
432
+ end
433
+
434
+ # alle Wortteilungen durchprobiert und noch immer kein definitives Kompositum erkannt. Dann nehme besten Vorschlag.
435
+ if @suggestions[level].empty?
436
+ [[],[],'']
437
+ else
438
+ stats, lexis, seqs = @suggestions[level][0]
439
+ @suggestions[level].clear
440
+ [stats, lexis, seqs]
441
+ end
442
+ end
443
+ end
444
+
445
+ # test_compositum() -> [stats, lexicals, seq]
446
+ #
447
+ # Testet einen definiert zerlegten String auf Kompositum
448
+ def test_compositum(front_string, infix, back_string, level, has_tail)
449
+ # Statistik merken für Validitätsprüfung
450
+ stats = [front_string.size, back_string.size]
451
+ seqs = ['?', '?']
452
+
453
+ # zuerst hinteren Teil auflösen
454
+ # 1. Möglichkeit: Wort mit oder ohne Suffix
455
+ back_lexicals = @dictionary.select_with_suffix(back_string)
456
+ unless back_lexicals.empty?
457
+ back_form = has_tail ? back_string : back_lexicals.sort[0].form
458
+ seqs[1] = back_lexicals.sort[0].attr
459
+ end
460
+
461
+ # 2. Möglichkeit: Wort mit oder ohne Infix, wenn es nicht der letzte Teil des Wortes ist
462
+ if back_lexicals.empty? && has_tail
463
+ back_lexicals = @dictionary.select_with_infix(back_string)
464
+ unless back_lexicals.empty?
465
+ back_form = back_string
466
+ seqs[1] = back_lexicals.sort[0].attr
467
+ end
468
+ end
469
+
470
+ # 3. Möglichkeit: Selber ein Kompositum (nur im Bindestrich-Fall!)
471
+ if back_lexicals.empty? && infix=='-'
472
+ back_stats, back_lexicals, back_seqs = find_compositum(back_string, level+1, has_tail)
473
+ unless back_lexicals.empty?
474
+ back_form = back_lexicals.sort[0].form
475
+ seqs[1] = back_seqs
476
+ stats = stats[0..0] + back_stats
477
+ end
478
+ end
479
+
480
+ # 4. Möglichkeit: Take it as is [Nimm's, wie es ist] (nur im Bindestrich-Fall!)
481
+ if back_lexicals.empty? && infix=='-'
482
+ back_lexicals = [Lexical.new(back_string, LA_TAKEITASIS)]
483
+ back_form = back_string
484
+ seqs[1] = back_lexicals.sort[0].attr
485
+ end
486
+
487
+ # wenn immer noch nicht erkannt, dann sofort zurück
488
+ return [[],[],''] if back_lexicals.empty?
489
+
490
+ # dann vorderen Teil auflösen
491
+ #
492
+ # 1. Möglichkeit: Wort mit oder ohne Infix
493
+ front_lexicals = @dictionary.select_with_infix(front_string)
494
+ unless front_lexicals.empty?
495
+ front_form = front_string
496
+ seqs[0] = front_lexicals.sort[0].attr
497
+ end
498
+
499
+ # 2. Möglichkeit: Selber ein Kompositum
500
+ if front_lexicals.empty?
501
+ front_stats, front_lexicals, front_seqs = find_compositum(front_string, level+1, true)
502
+ unless front_lexicals.empty?
503
+ front_form = front_lexicals.sort[0].form
504
+ seqs[0] = front_seqs
505
+ stats = front_stats + stats[1..-1]
506
+ end
507
+ end
508
+
509
+ # 3. Möglichkeit: Take it as is [Nimm's, wie es ist] (nur im Bindestrich-Fall!)
510
+ if front_lexicals.empty? && infix=='-'
511
+ front_lexicals = [Lexical.new(front_string, LA_TAKEITASIS)]
512
+ seqs[0] = front_lexicals.sort[0].attr
513
+ front_form = front_string
514
+ end
515
+
516
+ # wenn immer noch nicht erkannt, dann sofort zurück
517
+ return [[],[],''] if front_lexicals.empty?
518
+
519
+ # Kompositum gefunden, Grundform bilden
520
+ lexis = (front_lexicals + back_lexicals).collect { |lex|
521
+ (lex.attr==LA_KOMPOSITUM) ? nil : lex
522
+ }.compact
523
+ lexis << Lexical.new(front_form + infix + back_form, LA_KOMPOSITUM)
524
+
525
+ [stats, lexis.sort, seqs.join ]
526
+ end
527
+
528
+ end
529
+
530
+ end