lingo 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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