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,111 @@
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
+ class Lingo
31
+
32
+ # Die Attendees von Lingo übergeben Daten über ihre Kommunikationskanäle und entweder kommt bei
33
+ # einer komplexen Konfiguration hinten das gewünschte Ergebnis raus oder aber auch nicht. Für den
34
+ # letzeren Fall ist der Debugger primär gedacht. Er kann an beliebige Stelle in den Datenstrom
35
+ # eingeschleust werden, um Schritt für Schritt zu schauen, durch welchen Attendee das Ergebnis
36
+ # verfälscht wird um so den Fehler einzugrenzen und schließlich zu lösen.
37
+ #
38
+ # Der Debugger wird jedoch auch gerne für die Verfolgung der Verarbeitung am Bildschirm verwendet.
39
+ #
40
+ # Achtung: Um Irritationen bei der Anwendung mehrerer Debugger zu vermeiden wird empfohlen, den
41
+ # Debugger in der Konfiguration immer unmittelbar nach dem Attendee zu platzieren, dessen Ausgabe
42
+ # debugt werden soll. Ansonsten kann es zu scheinbar unerklärlichen Interferrenzen bei der Ausgabe
43
+ # kommen.
44
+ #
45
+ # === Mögliche Verlinkung
46
+ # Erwartet:: Daten beliebigen Typs
47
+ #
48
+ # === Parameter
49
+ # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
50
+ # Alle anderen Parameter müssen zwingend angegeben werden.
51
+ # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
52
+ # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
53
+ # <b><i>eval</i></b>:: (Standard: true) Gibt eine Bedingung an, die erfüllt sein muss, damit ein
54
+ # Datenobjekt ausgegeben wird (siehe Beschreibung Objectfilter)
55
+ # <b><i>ceval</i></b>:: (Standard: true) Gibt eiune Bedingung an, die erfüllt sein muss, damit ein
56
+ # Kommandoobjekt ausgegeben wird.
57
+ # <b><i>prompt</i></b>:: (Standard: 'lex:) ') Gibt an, mit welchem Prefix die Ausgabe versehen werden
58
+ # soll. Insbesondere wenn mit mehreren Debuggern gearbeitet wird, sollte dies
59
+ # genutzt werden.
60
+ #
61
+ # === Beispiele
62
+ # Bei der Verarbeitung der oben angegebenen Funktionsbeschreibung des Textwriters mit der Ablaufkonfiguration <tt>t1.cfg</tt>
63
+ # meeting:
64
+ # attendees:
65
+ # - textreader: { out: lines, files: '$(files)' }
66
+ # - debugger: { in: lines, prompt: 'LINES:) ' }
67
+ # - tokenizer: { in: lines, out: token }
68
+ # - debugger: { in: token, prompt: 'TOKEN:) ' }
69
+ # ergibt die Ausgabe
70
+ # LINES:) *FILE('test.txt')
71
+ # TOKEN:) *FILE('test.txt')
72
+ # LINES:) "Der Debugger kann was."
73
+ # TOKEN:) :Der/WORD:
74
+ # TOKEN:) :Debugger/WORD:
75
+ # TOKEN:) :kann/WORD:
76
+ # TOKEN:) :was/WORD:
77
+ # TOKEN:) :./PUNC:
78
+ # TOKEN:) *EOL('test.txt')
79
+ # LINES:) "Lingo auch :o)"
80
+ # TOKEN:) :Lingo/WORD:
81
+ # TOKEN:) :auch/WORD:
82
+ # TOKEN:) ::/PUNC:
83
+ # TOKEN:) :o/WORD:
84
+ # TOKEN:) :)/OTHR:
85
+ # TOKEN:) *EOL('test.txt')
86
+ # LINES:) *EOF('test.txt')
87
+ # TOKEN:) *EOF('test.txt')
88
+
89
+ class Attendee::Debugger < Attendee
90
+
91
+ protected
92
+
93
+ def init
94
+ @obj_eval = get_key('eval', 'true')
95
+ @cmd_eval = get_key('ceval', 'true')
96
+ @prompt = get_key('prompt', 'lex:) ')
97
+ end
98
+
99
+ def control(cmd, par)
100
+ if cmd!=STR_CMD_STATUS
101
+ @lingo.config.stderr.puts "#{@prompt} #{AgendaItem.new(cmd, par).inspect}" if eval(@cmd_eval)
102
+ end
103
+ end
104
+
105
+ def process(obj)
106
+ @lingo.config.stderr.puts "#{@prompt} #{obj.inspect}" if eval(@obj_eval)
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,101 @@
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
+ class Lingo
31
+
32
+ # Komposita, also zusammengesetzte Wörter, sind eine Spezialität der deutschen Sprache
33
+ # (z.B. Indexierungssystem oder Kompositumerkennung).
34
+ # Könnte man alle Kombinationen in den Wörterbüchern hinterlegen, dann würde der
35
+ # Wordsearcher die Erkennung bereits erledigt haben. Die hohe Anzahl der möglichen
36
+ # Kombinationen verbietet jedoch einen solchen Ansatz aufgrund des immensen Pflegeaufwands,
37
+ # eine algorithmische Lösung erscheint sinnvoller.
38
+ # Der Decomposer wertet alle vom Wordsearcher nicht erkannten Wörter aus und prüft sie
39
+ # auf Kompositum.
40
+ #
41
+ # === Mögliche Verlinkung
42
+ # Erwartet:: Daten vom Typ *Word* (andere werden einfach durchgereicht) z.B. von Wordsearcher
43
+ # Erzeugt:: Daten vom Typ *Word* (erkannte Komposita werden entsprechend erweitert) z.B. für Synonymer, Ocr_variator, Multiworder, Sequencer, Noneword_filter, Vector_filter
44
+ #
45
+ # === Parameter
46
+ # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
47
+ # Alle anderen Parameter müssen zwingend angegeben werden.
48
+ # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
49
+ # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
50
+ # <b>source</b>:: siehe allgemeine Beschreibung des Dictionary
51
+ # <b><i>mode</i></b>:: (Standard: all) siehe allgemeine Beschreibung des Dictionary
52
+ #
53
+ # === Beispiele
54
+ # Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
55
+ # meeting:
56
+ # attendees:
57
+ # - textreader: { out: lines, files: '$(files)' }
58
+ # - tokenizer: { in: lines, out: token }
59
+ # - abbreviator: { in: token, out: abbrev, source: 'sys-abk' }
60
+ # - wordsearcher: { in: abbrev, out: words, source: 'sys-dic' }
61
+ # - decomposer: { in: words, out: comps, source: 'sys-dic' }
62
+ # - debugger: { in: comps, prompt: 'out>' }
63
+ # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
64
+ # out> *FILE('test.txt')
65
+ # out> <Lingo|?>
66
+ # out> :,/PUNC:
67
+ # out> <ein = [(ein/w)]>
68
+ # out> <Indexierungssystem|KOM = [(indexierungssystem/k), (indexierung/s), (system/s)]>
69
+ # out> <mit = [(mit/w)]>
70
+ # out> <Kompositumerkennung|KOM = [(kompositumerkennung/k), (erkennung/s), (kompositum/s)]>
71
+ # out> :./PUNC:
72
+ # out> *EOL('test.txt')
73
+ # out> *EOF('test.txt')
74
+
75
+ class Attendee::Decomposer < Attendee
76
+
77
+ protected
78
+
79
+ def init
80
+ # Wörterbuch bereitstellen
81
+ src = get_array('source')
82
+ mod = get_key('mode', 'all')
83
+ @grammar = Grammar.new({'source'=>src, 'mode'=>mod}, @lingo)
84
+ end
85
+
86
+ def control(cmd, par)
87
+ @grammar.report.each_pair { |key, value|
88
+ set(key, value)
89
+ } if cmd == STR_CMD_STATUS
90
+ end
91
+
92
+ def process(obj)
93
+ if obj.is_a?(Word) && obj.attr == WA_UNKNOWN
94
+ obj = @grammar.find_compositum(obj.form)
95
+ end
96
+ forward(obj)
97
+ end
98
+
99
+ end
100
+
101
+ end
@@ -0,0 +1,167 @@
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
+ class Lingo
31
+
32
+ # Der Dehyphenizer ... muss noch dokumentiert werden
33
+ #
34
+ # === Mögliche Verlinkung
35
+ # Erwartet:: Daten vom Typ *Word* z.B. von Wordsearcher, Decomposer, Ocr_variator, Multiworder
36
+ # Erzeugt:: Daten vom Typ *Word* (mit Attribut WA_MULTIWORD). Je erkannter Mehrwortgruppe wird ein zusätzliches Word-Objekt in den Datenstrom eingefügt. Z.B. für Ocr_variator, Sequencer, Noneword_filter, Vector_filter
37
+ #
38
+ # === Parameter
39
+ # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
40
+ # Alle anderen Parameter müssen zwingend angegeben werden.
41
+ # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
42
+ # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
43
+ # <b>source</b>:: siehe allgemeine Beschreibung des Dictionary
44
+ # <b><i>mode</i></b>:: (Standard: all) siehe allgemeine Beschreibung des Dictionary
45
+ # <b><i>stopper</i></b>:: (Standard: TA_PUNCTUATION, TA_OTHER) Gibt die Begrenzungen an, zwischen
46
+ # denen der Multiworder suchen soll, i.d.R. Satzzeichen und Sonderzeichen,
47
+ # weil sie kaum in einer Mehrwortgruppen vorkommen.
48
+ #
49
+ # === Beispiele
50
+ # Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
51
+ # meeting:
52
+ # attendees:
53
+ # - textreader: { out: lines, files: '$(files)' }
54
+ # - tokenizer: { in: lines, out: token }
55
+ # - abbreviator: { in: token, out: abbrev, source: 'sys-abk' }
56
+ # - wordsearcher: { in: abbrev, out: words, source: 'sys-dic' }
57
+ # - decomposer: { in: words, out: comps, source: 'sys-dic' }
58
+ # - multiworder: { in: comps, out: multi, source: 'sys-mul' }
59
+ # - debugger: { in: multi, prompt: 'out>' }
60
+ # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
61
+ # out> *FILE('test.txt')
62
+ # out> <Sein = [(sein/s), (sein/v)]>
63
+ # out> <Name = [(name/s)]>
64
+ # out> <ist = [(sein/v)]>
65
+ # out> <johann van siegen|MUL = [(johann van siegen/m)]>
66
+ # out> <Johann = [(johann/e)]>
67
+ # out> <van = [(van/w)]>
68
+ # out> <Siegen = [(sieg/s), (siegen/v), (siegen/e)]>
69
+ # out> :./PUNC:
70
+ # out> *EOL('test.txt')
71
+ # out> *EOF('test.txt')
72
+
73
+ class Attendee::Dehyphenizer < BufferedAttendee
74
+
75
+ protected
76
+
77
+ def init
78
+ # Parameter verwerten
79
+ @stopper = get_array('stopper', TA_PUNCTUATION+','+TA_OTHER).collect {|s| s.upcase }
80
+
81
+ # Wörterbuch bereitstellen
82
+ src = get_array('source')
83
+ mod = get_key('mode', 'all')
84
+ @dic = Dictionary.new({'source'=>src, 'mode'=>mod}, @lingo)
85
+ @gra = Grammar.new({'source'=>src, 'mode'=>mod}, @lingo)
86
+
87
+ @number_of_expected_tokens_in_buffer = 2
88
+ @eof_handling = false
89
+
90
+ @skip = get_array('skip', "").collect { |wc| wc.downcase }
91
+ end
92
+
93
+ def control(cmd, par)
94
+ @dic.report.each_pair { |key, value| set(key, value) } if cmd == STR_CMD_STATUS
95
+
96
+ # Jedes Control-Object ist auch Auslöser der Verarbeitung
97
+ if cmd == STR_CMD_RECORD || cmd == STR_CMD_EOF
98
+ @eof_handling = true
99
+ while number_of_valid_tokens_in_buffer > 1
100
+ process_buffer
101
+ end
102
+ forward_number_of_token( @buffer.size, false )
103
+ @eof_handling = false
104
+ end
105
+ end
106
+
107
+ def process_buffer?
108
+ number_of_valid_tokens_in_buffer >= @number_of_expected_tokens_in_buffer
109
+ end
110
+
111
+ def process_buffer
112
+ if @buffer[0].is_a?(Word) &&
113
+ @buffer[0].form[-1..-1] == '-' &&
114
+ @buffer[1].is_a?(Word) &&
115
+ !(!( ttt = @buffer[1].get_class(/./) ).nil? &&
116
+ !@skip.index( ttt[0].attr ).nil?)
117
+
118
+ # Einfache Zusammensetzung versuchen
119
+ form = @buffer[0].form[0...-1] + @buffer[1].form
120
+ word = @dic.find_word( form )
121
+ word = @gra.find_compositum( form ) unless word.attr == WA_IDENTIFIED
122
+
123
+ unless word.attr == WA_IDENTIFIED || (word.attr == WA_KOMPOSITUM && word.get_class('x+').empty?)
124
+ # Zusammensetzung mit Bindestrich versuchen
125
+ form = @buffer[0].form + @buffer[1].form
126
+ word = @dic.find_word( form )
127
+ word = @gra.find_compositum( form ) unless word.attr == WA_IDENTIFIED
128
+ end
129
+
130
+ unless word.attr == WA_IDENTIFIED || (word.attr == WA_KOMPOSITUM && word.get_class('x+').empty?)
131
+ # Zusammensetzung mit Bindestrich versuchen
132
+ form = @buffer[0].form + @buffer[1].form
133
+ word = @dic.find_word( form )
134
+ word = @gra.find_compositum( form ) unless word.attr == WA_IDENTIFIED
135
+ end
136
+
137
+ if word.attr == WA_IDENTIFIED || (word.attr == WA_KOMPOSITUM && word.get_class('x+').empty?)
138
+ @buffer[0] = word
139
+ @buffer.delete_at( 1 )
140
+ end
141
+ end
142
+
143
+ # Buffer weiterschaufeln
144
+ forward_number_of_token( 1, false )
145
+ end
146
+
147
+ private
148
+
149
+ # Leitet 'len' Token weiter
150
+ def forward_number_of_token( len, count_punc = true )
151
+ begin
152
+ unless @buffer.empty?
153
+ forward( @buffer[0] )
154
+ len -= 1 unless count_punc && @buffer[0].form == CHAR_PUNCT
155
+ @buffer.delete_at( 0 )
156
+ end
157
+ end while len > 0
158
+ end
159
+
160
+ # Liefert die Anzahl gültiger Token zurück
161
+ def number_of_valid_tokens_in_buffer
162
+ @buffer.collect { |token| (token.form == CHAR_PUNCT) ? nil : 1 }.compact.size
163
+ end
164
+
165
+ end
166
+
167
+ end
@@ -0,0 +1,301 @@
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
+ class Lingo
31
+
32
+ # Mit der bisher beschriebenen Vorgehensweise werden die durch den Tokenizer erkannten
33
+ # Token aufgelöst und in Words verwandelt und über den Abbreviator und Decomposer auch
34
+ # Spezialfälle behandelt, die einzelne Wörter betreffen.
35
+ # Um jedoch auch Namen wie z.B. John F. Kennedy als Sinneinheit erkennen zu können, muss
36
+ # eine Analyse über mehrere Objekte erfolgen. Dies ist die Hauptaufgabe des Multiworders.
37
+ # Der Multiworder analysiert die Teile des Datenstroms, die z.B. durch Satzzeichen oder
38
+ # weiteren Einzelzeichen (z.B. '(') begrenzt sind. Erkannte Mehrwortgruppen werden als
39
+ # zusätzliches Objekt in den Datenstrom mit eingefügt.
40
+ #
41
+ # === Mögliche Verlinkung
42
+ # Erwartet:: Daten vom Typ *Word* z.B. von Wordsearcher, Decomposer, Ocr_variator, Multiworder
43
+ # Erzeugt:: Daten vom Typ *Word* (mit Attribut WA_MULTIWORD). Je erkannter Mehrwortgruppe wird ein zusätzliches Word-Objekt in den Datenstrom eingefügt. Z.B. für Ocr_variator, Sequencer, Noneword_filter, Vector_filter
44
+ #
45
+ # === Parameter
46
+ # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
47
+ # Alle anderen Parameter müssen zwingend angegeben werden.
48
+ # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
49
+ # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
50
+ # <b>source</b>:: siehe allgemeine Beschreibung des Dictionary
51
+ # <b><i>mode</i></b>:: (Standard: all) siehe allgemeine Beschreibung des Dictionary
52
+ # <b><i>stopper</i></b>:: (Standard: TA_PUNCTUATION, TA_OTHER) Gibt die Begrenzungen an, zwischen
53
+ # denen der Multiworder suchen soll, i.d.R. Satzzeichen und Sonderzeichen,
54
+ # weil sie kaum in einer Mehrwortgruppen vorkommen.
55
+ #
56
+ # === Beispiele
57
+ # Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
58
+ # meeting:
59
+ # attendees:
60
+ # - textreader: { out: lines, files: '$(files)' }
61
+ # - tokenizer: { in: lines, out: token }
62
+ # - abbreviator: { in: token, out: abbrev, source: 'sys-abk' }
63
+ # - wordsearcher: { in: abbrev, out: words, source: 'sys-dic' }
64
+ # - decomposer: { in: words, out: comps, source: 'sys-dic' }
65
+ # - multiworder: { in: comps, out: multi, source: 'sys-mul' }
66
+ # - debugger: { in: multi, prompt: 'out>' }
67
+ # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
68
+ # out> *FILE('test.txt')
69
+ # out> <Sein = [(sein/s), (sein/v)]>
70
+ # out> <Name = [(name/s)]>
71
+ # out> <ist = [(sein/v)]>
72
+ # out> <johann van siegen|MUL = [(johann van siegen/m)]>
73
+ # out> <Johann = [(johann/e)]>
74
+ # out> <van = [(van/w)]>
75
+ # out> <Siegen = [(sieg/s), (siegen/v), (siegen/e)]>
76
+ # out> :./PUNC:
77
+ # out> *EOL('test.txt')
78
+ # out> *EOF('test.txt')
79
+
80
+ class Attendee::Multiworder < BufferedAttendee
81
+
82
+ protected
83
+
84
+ def init
85
+ # Parameter verwerten
86
+ @stopper = get_array('stopper', TA_PUNCTUATION+','+TA_OTHER).collect {|s| s.upcase }
87
+
88
+ # Wörterbuch bereitstellen
89
+ mul_src = get_array('source')
90
+ mul_mod = get_key('mode', 'all')
91
+ @mul_dic = Dictionary.new({'source'=>mul_src, 'mode'=>mul_mod}, @lingo)
92
+
93
+ # combine lexical variants?
94
+ #
95
+ # false = old behaviour
96
+ # true = first match
97
+ # 'all' = all matches
98
+ @combine = get_key('combine', false)
99
+ @all_keys = @combine.is_a?(String) && @combine.downcase == 'all'
100
+
101
+ # Lexikalisierungs-Wörterbuch aus angegebenen Quellen ermitteln
102
+ lex_src, lex_mod, databases = nil, nil, @lingo.dictionary_config['databases']
103
+ mul_src.each { |src|
104
+ this_src, this_mod = databases[src].values_at('use-lex', 'lex-mode')
105
+ if lex_src.nil? || lex_src==this_src
106
+ lex_src, lex_mod = this_src, this_mod
107
+ else
108
+ forward(STR_CMD_WARN, "Die Mehrwortwörterbücher #{mul_src.join(',')} sind mit unterschiedlichen Wörterbüchern lexikalisiert worden")
109
+ end
110
+ }
111
+ lex_mod = get_key('lex-mode', lex_mod || 'first')
112
+ @lex_dic = Dictionary.new({'source'=>lex_src.split(STRING_SEPERATOR_PATTERN), 'mode'=>lex_mod}, @lingo)
113
+ @lex_gra = Grammar.new({'source'=>lex_src.split(STRING_SEPERATOR_PATTERN), 'mode'=>lex_mod}, @lingo)
114
+
115
+ if @combine && has_key?('use-syn')
116
+ syn_src = get_array('use-syn')
117
+ syn_mod = get_key('syn-mode', 'all')
118
+ @syn_dic = Dictionary.new({'source'=>syn_src, 'mode'=>syn_mod}, @lingo)
119
+ end
120
+
121
+ @number_of_expected_tokens_in_buffer = 3
122
+ @eof_handling = false
123
+ end
124
+
125
+ def control(cmd, par)
126
+ @mul_dic.report.each_pair { |key, value| set(key, value) } if cmd == STR_CMD_STATUS
127
+
128
+ # Jedes Control-Object ist auch Auslöser der Verarbeitung
129
+ if cmd == STR_CMD_RECORD || cmd == STR_CMD_EOF
130
+ @eof_handling = true
131
+ while number_of_valid_tokens_in_buffer > 1
132
+ process_buffer
133
+ end
134
+ forward_number_of_token( @buffer.size, false )
135
+ @eof_handling = false
136
+ end
137
+ end
138
+
139
+ def process_buffer?
140
+ number_of_valid_tokens_in_buffer >= @number_of_expected_tokens_in_buffer
141
+ end
142
+
143
+ def process_buffer
144
+ unless @buffer[0].form == CHAR_PUNCT
145
+ # Prüfe 3er Schlüssel
146
+ result = check_multiword_key( 3 )
147
+ unless result.empty?
148
+ # 3er Schlüssel gefunden
149
+ lengths = sort_result_len( result )
150
+ unless lengths[0] > 3
151
+ # Längster erkannter Schlüssel = 3
152
+ create_and_forward_multiword( 3, result )
153
+ forward_number_of_token( 3 )
154
+ return
155
+ else
156
+ # Längster erkannter Schlüssel > 3, Buffer voll genug?
157
+ unless @buffer.size >= lengths[0] || @eof_handling
158
+ @number_of_expected_tokens_in_buffer = lengths[0]
159
+ return
160
+ else
161
+ # Buffer voll genug, Verarbeitung kann beginnen
162
+ catch( :forward_one ) do
163
+ lengths.each do |len|
164
+ result = check_multiword_key( len )
165
+ unless result.empty?
166
+ create_and_forward_multiword( len, result )
167
+ forward_number_of_token( len )
168
+ throw :forward_one
169
+ end
170
+ end
171
+
172
+ # Keinen Match gefunden
173
+ forward_number_of_token( 1 )
174
+ end
175
+
176
+ @number_of_expected_tokens_in_buffer = 3
177
+ process_buffer if process_buffer?
178
+ return
179
+ end
180
+ end
181
+ end
182
+
183
+ # Prüfe 2er Schlüssel
184
+ result = check_multiword_key( 2 )
185
+ unless result.empty?
186
+ create_and_forward_multiword( 2, result )
187
+ forward_number_of_token( 1 )
188
+ end
189
+ end
190
+
191
+ # Buffer weiterschaufeln
192
+ forward_number_of_token( 1, false )
193
+ @number_of_expected_tokens_in_buffer = 3
194
+ end
195
+
196
+ private
197
+
198
+ def create_and_forward_multiword( len, lexicals )
199
+ # Form aus Buffer auslesen und Teile markieren
200
+ pos = 0
201
+ form_parts = []
202
+ begin
203
+ if @buffer[pos].form == CHAR_PUNCT
204
+ @buffer.delete_at( pos )
205
+ form_parts[-1] += CHAR_PUNCT
206
+ else
207
+ @buffer[pos].attr = WA_UNKMULPART if @buffer[pos].attr == WA_UNKNOWN
208
+ form_parts << @buffer[pos].form
209
+ pos += 1
210
+ end
211
+ end while pos < len
212
+
213
+ form = form_parts.join( ' ' )
214
+
215
+ # Multiword erstellen
216
+ word = Word.new( form, WA_MULTIWORD )
217
+ word << lexicals.collect { |lex| (lex.is_a?(Lexical)) ? lex : nil }.compact # FIXME 1.60 - Ausstieg bei "*5" im Synonymer
218
+
219
+ # Forword Multiword
220
+ forward( word )
221
+ end
222
+
223
+ # Leitet 'len' Token weiter
224
+ def forward_number_of_token( len, count_punc = true )
225
+ begin
226
+ unless @buffer.empty?
227
+ forward( @buffer[0] )
228
+ len -= 1 unless count_punc && @buffer[0].form == CHAR_PUNCT
229
+ @buffer.delete_at( 0 )
230
+ end
231
+ end while len > 0
232
+ end
233
+
234
+ # Ermittelt die maximale Ergebnislänge
235
+ def sort_result_len( result )
236
+ result.collect do |res|
237
+ if res.is_a?( Lexical )
238
+ res.form.split( ' ' ).size
239
+ else
240
+ res =~ /^\*(\d+)/
241
+ $1.to_i
242
+ end
243
+ end.sort.reverse
244
+ end
245
+
246
+ # Prüft einen definiert langen Schlüssel ab Position 0 im Buffer
247
+ def check_multiword_key( len )
248
+ return [] if number_of_valid_tokens_in_buffer < len
249
+
250
+ # Wortformen aus der Wortliste auslesen
251
+ sequence = @buffer.map { |obj|
252
+ next [obj] unless obj.is_a?(StringA)
253
+
254
+ form = obj.form
255
+ next if form == CHAR_PUNCT
256
+
257
+ word = @lex_dic.find_word(form)
258
+ word = @lex_gra.find_compositum(form) if word.attr == WA_UNKNOWN
259
+
260
+ lexicals = word.attr == WA_KOMPOSITUM ?
261
+ [word.lexicals.first] : word.lexicals.dup
262
+
263
+ lexicals << word if lexicals.empty?
264
+ lexicals += @syn_dic.find_synonyms(word) if @syn_dic
265
+
266
+ lexicals.map { |lex| lex.form }.uniq
267
+ }.compact[0, len]
268
+
269
+ if @combine
270
+ keys, muls = [], []
271
+
272
+ sequence.each { |forms|
273
+ keys = forms.map { |form|
274
+ keys.empty? ? form : keys.map { |key| "#{key} #{form}" }
275
+ }.flatten(1)
276
+ }
277
+
278
+ keys.each { |key|
279
+ mul = @mul_dic.select(key.downcase)
280
+
281
+ unless mul.empty?
282
+ muls.concat(mul)
283
+ break unless @all_keys
284
+ end
285
+ }
286
+
287
+ muls.uniq
288
+ else
289
+ key = sequence.map { |forms| forms.first }.join(' ')
290
+ @mul_dic.select(key.downcase)
291
+ end
292
+ end
293
+
294
+ # Liefert die Anzahl gültiger Token zurück
295
+ def number_of_valid_tokens_in_buffer
296
+ @buffer.collect { |token| (token.form == CHAR_PUNCT) ? nil : 1 }.compact.size
297
+ end
298
+
299
+ end
300
+
301
+ end