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,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