lingo 1.8.0 → 1.8.1
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.
- data/ChangeLog +13 -0
- data/README +49 -29
- data/Rakefile +28 -4
- data/TODO +2 -9
- data/bin/lingo +24 -0
- data/bin/lingoctl +24 -0
- data/de/lingo-dic.txt +559 -74
- data/info/gpl-hdr.txt +21 -24
- data/lib/lingo.rb +83 -112
- data/lib/lingo/agenda_item.rb +53 -0
- data/lib/lingo/attendee.rb +261 -0
- data/lib/lingo/attendee/abbreviator.rb +95 -97
- data/lib/lingo/attendee/debugger.rb +94 -93
- data/lib/lingo/attendee/decomposer.rb +76 -83
- data/lib/lingo/attendee/dehyphenizer.rb +141 -144
- data/lib/lingo/attendee/formatter.rb +65 -0
- data/lib/lingo/attendee/multi_worder.rb +302 -0
- data/lib/lingo/attendee/noneword_filter.rb +89 -84
- data/lib/lingo/attendee/object_filter.rb +91 -0
- data/lib/lingo/attendee/sequencer.rb +159 -158
- data/lib/lingo/attendee/synonymer.rb +81 -84
- data/lib/lingo/attendee/text_reader.rb +242 -0
- data/lib/lingo/attendee/text_writer.rb +169 -0
- data/lib/lingo/attendee/tokenizer.rb +192 -191
- data/lib/lingo/attendee/variator.rb +152 -156
- data/lib/lingo/attendee/vector_filter.rb +140 -135
- data/lib/lingo/attendee/word_searcher.rb +98 -0
- data/lib/lingo/buffered_attendee.rb +69 -0
- data/lib/lingo/cachable.rb +58 -0
- data/lib/lingo/call.rb +72 -0
- data/lib/lingo/cli.rb +26 -0
- data/lib/lingo/config.rb +23 -26
- data/lib/lingo/core_ext.rb +42 -0
- data/lib/lingo/ctl.rb +239 -173
- data/lib/lingo/database.rb +148 -496
- data/lib/lingo/database/crypter.rb +85 -0
- data/lib/lingo/database/gdbm_store.rb +49 -0
- data/lib/lingo/database/hash_store.rb +67 -0
- data/lib/lingo/database/libcdb_store.rb +58 -0
- data/lib/lingo/database/sdbm_store.rb +64 -0
- data/lib/lingo/database/show_progress.rb +81 -0
- data/lib/lingo/database/source.rb +134 -0
- data/lib/lingo/database/source/key_value.rb +62 -0
- data/lib/lingo/database/source/multi_key.rb +65 -0
- data/lib/lingo/database/source/multi_value.rb +65 -0
- data/lib/lingo/database/source/single_word.rb +60 -0
- data/lib/lingo/database/source/word_class.rb +64 -0
- data/lib/lingo/error.rb +122 -0
- data/lib/lingo/language.rb +78 -518
- data/lib/lingo/language/dictionary.rb +173 -0
- data/lib/lingo/language/grammar.rb +211 -0
- data/lib/lingo/language/lexical.rb +66 -0
- data/lib/lingo/language/lexical_hash.rb +88 -0
- data/lib/lingo/language/token.rb +48 -0
- data/lib/lingo/language/word.rb +130 -0
- data/lib/lingo/language/word_form.rb +83 -0
- data/lib/lingo/reportable.rb +59 -0
- data/lib/lingo/version.rb +1 -1
- data/lingo-all.cfg +14 -10
- data/lingo-call.cfg +5 -5
- data/lingo.cfg +14 -12
- data/lingo.rb +26 -0
- data/lir.cfg +13 -9
- data/spec/spec_helper.rb +1 -0
- data/test.cfg +11 -11
- data/test/attendee/ts_abbreviator.rb +0 -6
- data/test/attendee/ts_decomposer.rb +0 -6
- data/test/attendee/{ts_multiworder.rb → ts_multi_worder.rb} +1 -7
- data/test/attendee/ts_noneword_filter.rb +1 -7
- data/test/attendee/{ts_objectfilter.rb → ts_object_filter.rb} +1 -7
- data/test/attendee/ts_sequencer.rb +0 -6
- data/test/attendee/ts_synonymer.rb +0 -6
- data/test/attendee/{ts_textreader.rb → ts_text_reader.rb} +1 -7
- data/test/attendee/{ts_textwriter.rb → ts_text_writer.rb} +1 -7
- data/test/attendee/ts_tokenizer.rb +0 -6
- data/test/attendee/ts_variator.rb +0 -6
- data/test/attendee/ts_vector_filter.rb +1 -7
- data/test/attendee/{ts_wordsearcher.rb → ts_word_searcher.rb} +1 -7
- data/test/ref/artikel.non +2 -29
- data/test/ref/artikel.seq +13 -8
- data/test/ref/artikel.vec +30 -15
- data/test/ref/artikel.ven +29 -14
- data/test/ref/artikel.ver +58 -43
- data/test/ref/lir.csv +146 -145
- data/test/ref/lir.non +186 -210
- data/test/ref/lir.seq +54 -50
- data/test/test_helper.rb +41 -36
- data/test/ts_database.rb +12 -11
- data/test/ts_language.rb +118 -68
- metadata +67 -29
- data/lib/lingo/attendee/multiworder.rb +0 -301
- data/lib/lingo/attendee/objectfilter.rb +0 -86
- data/lib/lingo/attendee/textreader.rb +0 -237
- data/lib/lingo/attendee/textwriter.rb +0 -196
- data/lib/lingo/attendee/wordsearcher.rb +0 -96
- data/lib/lingo/attendees.rb +0 -289
- data/lib/lingo/const.rb +0 -131
- data/lib/lingo/modules.rb +0 -98
- data/lib/lingo/types.rb +0 -285
- data/lib/lingo/utilities.rb +0 -40
|
@@ -1,216 +1,217 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# Copyright (C) 2007
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
# welcomeATlex-lingoDOTde near 50°55'N+6°55'E.
|
|
26
|
-
#
|
|
27
|
-
# Lex Lingo rules from here on
|
|
4
|
+
###############################################################################
|
|
5
|
+
# #
|
|
6
|
+
# Lingo -- A full-featured automatic indexing system #
|
|
7
|
+
# #
|
|
8
|
+
# Copyright (C) 2005-2007 John Vorhauer #
|
|
9
|
+
# Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
|
|
10
|
+
# #
|
|
11
|
+
# Lingo is free software; you can redistribute it and/or modify it under the #
|
|
12
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
|
13
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
|
14
|
+
# any later version. #
|
|
15
|
+
# #
|
|
16
|
+
# Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
|
|
17
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
|
18
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
|
19
|
+
# more details. #
|
|
20
|
+
# #
|
|
21
|
+
# You should have received a copy of the GNU Affero General Public License #
|
|
22
|
+
# along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
|
|
23
|
+
# #
|
|
24
|
+
###############################################################################
|
|
28
25
|
#++
|
|
29
26
|
|
|
30
27
|
class Lingo
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
29
|
+
class Attendee
|
|
30
|
+
|
|
31
|
+
# Der Tokenizer zerlegt eine Textzeile in einzelne Token. Dies ist notwendig,
|
|
32
|
+
# damit nachfolgende Attendees die Textdatei häppchenweise verarbeiten können.
|
|
33
|
+
#
|
|
34
|
+
# === Mögliche Verlinkung
|
|
35
|
+
# Erwartet:: Daten des Typs *String* (Textzeilen) z.B. von TextReader
|
|
36
|
+
# Erzeugt:: Daten des Typs *Token* z.B. für Abbreviator, Wordsearcher
|
|
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
|
+
#
|
|
44
|
+
# === Konfiguration
|
|
45
|
+
# Der Tokenizer benötigt zur Identifikation einzelner Token Regeln, nach denen er
|
|
46
|
+
# arbeiten soll. Die benötigten Regeln werden aufgrund des Umfangs nicht als Parameter,
|
|
47
|
+
# sondern in der Sprachkonfiguration hinterlegt, die sich standardmäßig in der Datei
|
|
48
|
+
# <tt>de.lang</tt> befindet (YAML-Format).
|
|
49
|
+
# language:
|
|
50
|
+
# attendees:
|
|
51
|
+
# tokenizer:
|
|
52
|
+
# regulars:
|
|
53
|
+
# - _CHR_: '\wÄÖÜÁÂÀÉÊÈÍÎÌÓÔÒÚÛÙÝäöüáâàéêèíîìóôòúûùý'
|
|
54
|
+
# - NUMS: '[+-]?(\d{4,}|\d{1,3}(\.\d{3,3})*)(\.|(,\d+)?%?)'
|
|
55
|
+
# - URLS: '((mailto:|(news|http|https|ftp|ftps)://)\S+|^(www(\.\S+)+)|\S+([\._]\S+)+@\S+(\.\S+)+)'
|
|
56
|
+
# - ABRV: '(([_CHR_]+\.)+)[_CHR_]+'
|
|
57
|
+
# - ABRS: '(([_CHR_]{1,1}\.)+)(?!\.\.)'
|
|
58
|
+
# - WORD: '[_CHR_\d]+'
|
|
59
|
+
# - PUNC: '[!,\.:;?]'
|
|
60
|
+
# - OTHR: '[!\"#$%&()*\+,\-\./:;<=>?@\[\\\]^_`{|}~´]'
|
|
61
|
+
# - HELP: '.*'
|
|
62
|
+
# Die Regeln werden in der angegebenen Reihenfolge abgearbeitet, solange bis ein Token
|
|
63
|
+
# erkannt wurde. Sollte keine Regel zutreffen, so greift die letzt Regel +HELP+ in jedem
|
|
64
|
+
# Fall.
|
|
65
|
+
# Regeln, deren Name in Unterstriche eingefasst sind, werden als Makro interpretiert.
|
|
66
|
+
# Makros werden genutzt, um lange oder sich wiederholende Bestandteile von Regeln
|
|
67
|
+
# einmalig zu definieren und in den Regeln über den Makronamen eine Auflösung zu forcieren.
|
|
68
|
+
# Makros werden selber nicht für die Erkennung von Token eingesetzt.
|
|
69
|
+
#
|
|
70
|
+
# === Generierte Kommandos
|
|
71
|
+
# Damit der nachfolgende Datenstrom einwandfrei verarbeitet werden kann, generiert der Tokenizer
|
|
72
|
+
# Kommandos, die mit in den Datenstrom eingefügt werden.
|
|
73
|
+
# <b>*EOL(<dateiname>)</b>:: Kennzeichnet das Ende einer Textzeile, da die Information ansonsten
|
|
74
|
+
# für nachfolgende Attendees verloren wäre.
|
|
75
|
+
#
|
|
76
|
+
# === Beispiele
|
|
77
|
+
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
|
78
|
+
# meeting:
|
|
79
|
+
# attendees:
|
|
80
|
+
# - text_reader: { out: lines, files: '$(files)' }
|
|
81
|
+
# - tokenizer: { in: lines, out: token }
|
|
82
|
+
# - debugger: { in: token, prompt: 'out>' }
|
|
83
|
+
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
|
84
|
+
# out> *FILE('test.txt')
|
|
85
|
+
# out> :Dies/WORD:
|
|
86
|
+
# out> :ist/WORD:
|
|
87
|
+
# out> :eine/WORD:
|
|
88
|
+
# out> :Zeile/WORD:
|
|
89
|
+
# out> :./PUNC:
|
|
90
|
+
# out> *EOL('test.txt')
|
|
91
|
+
# out> :Dies/WORD:
|
|
92
|
+
# out> :ist/WORD:
|
|
93
|
+
# out> :noch/WORD:
|
|
94
|
+
# out> :eine/WORD:
|
|
95
|
+
# out> :./PUNC:
|
|
96
|
+
# out> *EOL('test.txt')
|
|
97
|
+
# out> *EOF('test.txt')
|
|
98
|
+
|
|
99
|
+
class Tokenizer < self
|
|
100
|
+
|
|
101
|
+
protected
|
|
102
|
+
|
|
103
|
+
def init
|
|
104
|
+
# Regular Expressions für Token-Erkennung einlesen
|
|
105
|
+
regulars = get_key('regulars', '')
|
|
106
|
+
raise NoConfigKeyError.new(:regulars) unless regulars
|
|
107
|
+
|
|
108
|
+
@space = get_key('space', false)
|
|
109
|
+
@tags = get_key('tags', true)
|
|
110
|
+
@wiki = get_key('wiki', true)
|
|
111
|
+
|
|
112
|
+
# default rules
|
|
113
|
+
@rules = [['SPAC', /^\s+/]]
|
|
114
|
+
@rules << ['HTML', /^<[^>]+>/] unless @tags
|
|
115
|
+
@rules << ['WIKI', /^\[\[.+?\]\]/] unless @wiki
|
|
116
|
+
@rules.unshift(['WIKI', /^=+.+=+$/]) unless @wiki
|
|
117
|
+
|
|
118
|
+
# Mit _xxx_ gekennzeichnete Makros anwenden und Expressions ergänzen und umwandeln
|
|
119
|
+
macros = {}
|
|
120
|
+
|
|
121
|
+
regulars.each { |rule|
|
|
122
|
+
name = rule.keys[0]
|
|
123
|
+
expr = rule.values[0].gsub(/_(\w+?)_/) {
|
|
124
|
+
macros[$&] || begin
|
|
125
|
+
Database::Source.const_get("UTF8_#{$1.upcase}")
|
|
126
|
+
rescue NameError
|
|
127
|
+
end
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if name =~ /^_\w+_$/ # is a macro
|
|
131
|
+
macros[name] = expr if name =~ /^_\w+_$/
|
|
132
|
+
else
|
|
133
|
+
@rules << [name, Regexp.new('^'+expr)]
|
|
128
134
|
end
|
|
129
135
|
}
|
|
130
136
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
# Der Tokenizer gibt jedes Zeilenende als Information weiter, sofern es sich
|
|
139
|
-
# nicht um die Verarbeitung einer LIR-Datei handelt. Im Falle einer normalen Datei
|
|
140
|
-
# wird der Dateiname gespeichert und als Kennzeichen für die Erzeugung von
|
|
141
|
-
# Zeilenende-Nachrichten herangezogen.
|
|
142
|
-
@filename = nil
|
|
143
|
-
end
|
|
137
|
+
# Der Tokenizer gibt jedes Zeilenende als Information weiter, sofern es sich
|
|
138
|
+
# nicht um die Verarbeitung einer LIR-Datei handelt. Im Falle einer normalen Datei
|
|
139
|
+
# wird der Dateiname gespeichert und als Kennzeichen für die Erzeugung von
|
|
140
|
+
# Zeilenende-Nachrichten herangezogen.
|
|
141
|
+
@filename = nil
|
|
142
|
+
end
|
|
144
143
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
def control(cmd, param)
|
|
145
|
+
case cmd
|
|
146
|
+
when STR_CMD_FILE then @filename = param
|
|
147
|
+
when STR_CMD_LIR then @filename = nil
|
|
148
|
+
when STR_CMD_EOF then @cont = nil
|
|
149
|
+
end
|
|
150
150
|
end
|
|
151
|
-
end
|
|
152
151
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
def process(obj)
|
|
153
|
+
if obj.is_a?(String)
|
|
154
|
+
inc('Anzahl Zeilen')
|
|
156
155
|
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
tokenize(obj) { |form, attr|
|
|
157
|
+
token = Token.new(form, attr)
|
|
159
158
|
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
inc('Anzahl Muster '+token.attr)
|
|
160
|
+
inc('Anzahl Token')
|
|
162
161
|
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
forward(token)
|
|
163
|
+
}
|
|
165
164
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
forward(STR_CMD_EOL, @filename) if @filename
|
|
166
|
+
else
|
|
167
|
+
forward(obj)
|
|
168
|
+
end
|
|
169
169
|
end
|
|
170
|
-
end
|
|
171
170
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
171
|
+
private
|
|
172
|
+
|
|
173
|
+
# tokenize("Eine Zeile.") -> [:Eine/WORD:, :Zeile/WORD:, :./PUNC:]
|
|
174
|
+
def tokenize(textline)
|
|
175
|
+
case @cont
|
|
176
|
+
when 'HTML'
|
|
177
|
+
if textline =~ /^[^<>]*>/
|
|
178
|
+
yield $~[0], @cont
|
|
179
|
+
textline, @cont = $', nil
|
|
180
|
+
else
|
|
181
|
+
yield textline, @cont
|
|
182
|
+
return
|
|
183
|
+
end
|
|
184
|
+
when 'WIKI'
|
|
185
|
+
if textline =~ /^[^\[\]]*\]\]/
|
|
186
|
+
yield $~[0], @cont
|
|
187
|
+
textline, @cont = $', nil
|
|
188
|
+
else
|
|
189
|
+
yield textline, @cont
|
|
190
|
+
return
|
|
191
|
+
end
|
|
192
|
+
when nil
|
|
193
|
+
if !@tags && textline =~ /<[^<>]*$/
|
|
194
|
+
yield $~[0], @cont = 'HTML'
|
|
195
|
+
textline = $`
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
if !@wiki && textline =~ /\[\[[^\[\]]*$/
|
|
199
|
+
yield $~[0], @cont = 'WIKI'
|
|
200
|
+
textline = $`
|
|
201
|
+
end
|
|
202
|
+
end
|
|
198
203
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
textline
|
|
202
|
-
|
|
204
|
+
until textline.empty?
|
|
205
|
+
@rules.each { |name, expr|
|
|
206
|
+
if textline =~ expr
|
|
207
|
+
yield $~[0], name if name != 'SPAC' || @space
|
|
208
|
+
textline = $'
|
|
209
|
+
break
|
|
210
|
+
end
|
|
211
|
+
}
|
|
212
|
+
end
|
|
203
213
|
end
|
|
204
214
|
|
|
205
|
-
until textline.empty?
|
|
206
|
-
@rules.each { |name, expr|
|
|
207
|
-
if textline =~ expr
|
|
208
|
-
yield $~[0], name if name != 'SPAC' || @space
|
|
209
|
-
textline = $'
|
|
210
|
-
break
|
|
211
|
-
end
|
|
212
|
-
}
|
|
213
|
-
end
|
|
214
215
|
end
|
|
215
216
|
|
|
216
217
|
end
|
|
@@ -1,183 +1,179 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# Copyright (C) 2007
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
# welcomeATlex-lingoDOTde near 50°55'N+6°55'E.
|
|
26
|
-
#
|
|
27
|
-
# Lex Lingo rules from here on
|
|
4
|
+
###############################################################################
|
|
5
|
+
# #
|
|
6
|
+
# Lingo -- A full-featured automatic indexing system #
|
|
7
|
+
# #
|
|
8
|
+
# Copyright (C) 2005-2007 John Vorhauer #
|
|
9
|
+
# Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
|
|
10
|
+
# #
|
|
11
|
+
# Lingo is free software; you can redistribute it and/or modify it under the #
|
|
12
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
|
13
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
|
14
|
+
# any later version. #
|
|
15
|
+
# #
|
|
16
|
+
# Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
|
|
17
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
|
18
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
|
19
|
+
# more details. #
|
|
20
|
+
# #
|
|
21
|
+
# You should have received a copy of the GNU Affero General Public License #
|
|
22
|
+
# along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
|
|
23
|
+
# #
|
|
24
|
+
###############################################################################
|
|
28
25
|
#++
|
|
29
26
|
|
|
30
27
|
class Lingo
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
# Austausch einzelner Wortteile einchließlich erneuter Wörterbuchsuche zur
|
|
34
|
-
# Verbesserung der Worterkennungsquote.
|
|
35
|
-
#
|
|
36
|
-
# Ursprünglich wurde der Variator entwickelt, um die mangelnde Qualität bei der
|
|
37
|
-
# OCR-Erkennung altdeutscher 's'-Konsonanten zu optimieren. Er kann ebenso bei
|
|
38
|
-
# alternativen Umlautschreibweisen z.B. zur Wandlung von 'Koeln' in 'Köln' dienen.
|
|
39
|
-
#
|
|
40
|
-
# === Mögliche Verlinkung
|
|
41
|
-
# Erwartet:: Daten vom Typ *Word* (andere werden einfach durchgereicht) z.B. von Wordsearcher
|
|
42
|
-
# Erzeugt:: Daten vom Typ *Word* zur Weiterleitung z.B. an Synonymer, Decomposer, Multiworder, Sequencer, Noneword_filter oder Vector_filter
|
|
43
|
-
#
|
|
44
|
-
# === Parameter
|
|
45
|
-
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
|
46
|
-
# Alle anderen Parameter müssen zwingend angegeben werden.
|
|
47
|
-
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
|
48
|
-
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
|
49
|
-
# <b>source</b>:: siehe allgemeine Beschreibung des Dictionary
|
|
50
|
-
# <b><i>mode</i></b>:: (Standard: all) siehe allgemeine Beschreibung des Dictionary
|
|
51
|
-
# <b><i>^check</i></b>:: (Standard: WA_UNKNOWN) Gebrenzt die zu variierenden Worttypen
|
|
52
|
-
# <b><i>marker</i></b>:: (Standard: '*') Kennzeichnung durch Variation erkannter Wörter
|
|
53
|
-
# <b><i>max-var</i></b>:: (Standard: '10000') Begrenzung der maximal zu prüfenden Permutationen bei der vollständigen Kombination aller auf ein Wort anzuwendenen aufgelisteten Wortteile.
|
|
54
|
-
#
|
|
55
|
-
# === Beispiele
|
|
56
|
-
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
|
57
|
-
# meeting:
|
|
58
|
-
# attendees:
|
|
59
|
-
# - textreader: { out: lines, files: '$(files)' }
|
|
60
|
-
# - tokenizer: { in: lines, out: token }
|
|
61
|
-
# - wordsearcher: { in: abbrev, out: words, source: 'sys-dic' }
|
|
62
|
-
# - variator: { in: words, out: varios, source: 'sys-dic' }
|
|
63
|
-
# - debugger: { in: varios, prompt: 'out>' }
|
|
64
|
-
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
|
65
|
-
# out> *FILE('test.txt')
|
|
66
|
-
# out> <*Dies = [(dies/w)]>
|
|
67
|
-
# out> <*ist = [(ist/t)]>
|
|
68
|
-
# out> <ein = [(ein/t)]>
|
|
69
|
-
# out> <*Tisch = [(tisch/s)]>
|
|
70
|
-
# out> :./PUNC:
|
|
71
|
-
# out> *EOL('test.txt')
|
|
72
|
-
# out> *EOF('test.txt')
|
|
73
|
-
|
|
74
|
-
class Attendee::Variator < Attendee
|
|
75
|
-
|
|
76
|
-
protected
|
|
77
|
-
|
|
78
|
-
def init
|
|
79
|
-
# Parameter verarbeiten
|
|
80
|
-
@marker = get_key('marker', '*')
|
|
81
|
-
@max_var = get_key('max-var', '10000').to_i
|
|
82
|
-
filter = get_array('check', WA_UNKNOWN)
|
|
83
|
-
|
|
84
|
-
src = get_array('source')
|
|
85
|
-
mod = get_key('mode', 'all')
|
|
86
|
-
|
|
87
|
-
# Daten verarbeiten
|
|
88
|
-
@var_strings = get_key('variations')
|
|
89
|
-
forward(STR_CMD_ERR, 'Ocr-variator: Konfiguration <ocr-variator> ist leer') if @var_strings.size==0
|
|
90
|
-
|
|
91
|
-
# Initialisierungen
|
|
92
|
-
@check = Hash.new(false)
|
|
93
|
-
filter.each { |s| @check[s.upcase] = true }
|
|
94
|
-
|
|
95
|
-
# Wörterbuchzugriff
|
|
96
|
-
@dic = Dictionary.new({'source'=>src, 'mode'=>mod}, @lingo)
|
|
97
|
-
@gra = Grammar.new({'source'=>src, 'mode'=>mod}, @lingo)
|
|
98
|
-
|
|
99
|
-
# Optimierungen
|
|
100
|
-
if @max_var == 0
|
|
101
|
-
forward( STR_CMD_WARN, 'Ocr-variator: max-var ist 0, setze es auf 10.000' )
|
|
102
|
-
@max_var = 10000
|
|
103
|
-
end
|
|
104
|
-
end
|
|
29
|
+
class Attendee
|
|
105
30
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
31
|
+
# Der Variator ermöglicht bei nicht erkannten Wörtern den listenbasierten
|
|
32
|
+
# Austausch einzelner Wortteile einchließlich erneuter Wörterbuchsuche zur
|
|
33
|
+
# Verbesserung der Worterkennungsquote.
|
|
34
|
+
#
|
|
35
|
+
# Ursprünglich wurde der Variator entwickelt, um die mangelnde Qualität bei der
|
|
36
|
+
# OCR-Erkennung altdeutscher 's'-Konsonanten zu optimieren. Er kann ebenso bei
|
|
37
|
+
# alternativen Umlautschreibweisen z.B. zur Wandlung von 'Koeln' in 'Köln' dienen.
|
|
38
|
+
#
|
|
39
|
+
# === Mögliche Verlinkung
|
|
40
|
+
# Erwartet:: Daten vom Typ *Word* (andere werden einfach durchgereicht) z.B. von Wordsearcher
|
|
41
|
+
# Erzeugt:: Daten vom Typ *Word* zur Weiterleitung z.B. an Synonymer, Decomposer, Multiworder, Sequencer, Noneword_filter oder Vector_filter
|
|
42
|
+
#
|
|
43
|
+
# === Parameter
|
|
44
|
+
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
|
45
|
+
# Alle anderen Parameter müssen zwingend angegeben werden.
|
|
46
|
+
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
|
47
|
+
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
|
48
|
+
# <b>source</b>:: siehe allgemeine Beschreibung des Dictionary
|
|
49
|
+
# <b><i>mode</i></b>:: (Standard: all) siehe allgemeine Beschreibung des Dictionary
|
|
50
|
+
# <b><i>^check</i></b>:: (Standard: WA_UNKNOWN) Gebrenzt die zu variierenden Worttypen
|
|
51
|
+
# <b><i>marker</i></b>:: (Standard: '*') Kennzeichnung durch Variation erkannter Wörter
|
|
52
|
+
# <b><i>max-var</i></b>:: (Standard: '10000') Begrenzung der maximal zu prüfenden Permutationen bei der vollständigen Kombination aller auf ein Wort anzuwendenen aufgelisteten Wortteile.
|
|
53
|
+
#
|
|
54
|
+
# === Beispiele
|
|
55
|
+
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
|
56
|
+
# meeting:
|
|
57
|
+
# attendees:
|
|
58
|
+
# - text_reader: { out: lines, files: '$(files)' }
|
|
59
|
+
# - tokenizer: { in: lines, out: token }
|
|
60
|
+
# - word_searcher: { in: abbrev, out: words, source: 'sys-dic' }
|
|
61
|
+
# - variator: { in: words, out: varios, source: 'sys-dic' }
|
|
62
|
+
# - debugger: { in: varios, prompt: 'out>' }
|
|
63
|
+
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
|
64
|
+
# out> *FILE('test.txt')
|
|
65
|
+
# out> <*Dies = [(dies/w)]>
|
|
66
|
+
# out> <*ist = [(ist/t)]>
|
|
67
|
+
# out> <ein = [(ein/t)]>
|
|
68
|
+
# out> <*Tisch = [(tisch/s)]>
|
|
69
|
+
# out> :./PUNC:
|
|
70
|
+
# out> *EOL('test.txt')
|
|
71
|
+
# out> *EOF('test.txt')
|
|
72
|
+
|
|
73
|
+
class Variator < self
|
|
74
|
+
|
|
75
|
+
protected
|
|
76
|
+
|
|
77
|
+
def init
|
|
78
|
+
# Parameter verarbeiten
|
|
79
|
+
@marker = get_key('marker', '*')
|
|
80
|
+
@max_var = get_key('max-var', '10000').to_i
|
|
81
|
+
filter = get_array('check', WA_UNKNOWN)
|
|
82
|
+
|
|
83
|
+
# Daten verarbeiten
|
|
84
|
+
@var_strings = get_key('variations')
|
|
85
|
+
raise MissingConfigError.new(:variations) if @var_strings.empty?
|
|
86
|
+
|
|
87
|
+
# Initialisierungen
|
|
88
|
+
@check = Hash.new(false)
|
|
89
|
+
filter.each { |s| @check[s.upcase] = true }
|
|
90
|
+
|
|
91
|
+
set_dic
|
|
92
|
+
set_gra
|
|
93
|
+
|
|
94
|
+
if @max_var.zero?
|
|
95
|
+
@max_var = 10000
|
|
96
|
+
@lingo.warn "#{self.class}: max-var is 0, setting to #{@max_var}"
|
|
97
|
+
end
|
|
112
98
|
end
|
|
113
|
-
end
|
|
114
99
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
# Erzeuge Variationen einer Wortform
|
|
122
|
-
variations = [obj.form]
|
|
123
|
-
@var_strings.each do |switch|
|
|
124
|
-
from, to = switch
|
|
125
|
-
variations = variate(variations, from, to)
|
|
100
|
+
def control(cmd, par)
|
|
101
|
+
# Status wird abgefragt
|
|
102
|
+
if cmd == STR_CMD_STATUS
|
|
103
|
+
# Eigenen Status um Status von Dictionary und Grammer erweitern
|
|
104
|
+
@dic.report.each_pair { | k, v | set( k, v ) }
|
|
105
|
+
@gra.report.each_pair { | k, v | set( k, v ) }
|
|
126
106
|
end
|
|
107
|
+
end
|
|
127
108
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
109
|
+
def process(obj)
|
|
110
|
+
# Zu prüfende Wörter filtern
|
|
111
|
+
if obj.is_a?(Word) && @check[obj.attr]
|
|
112
|
+
# Statistik für Report
|
|
113
|
+
inc('Anzahl gesuchter Wörter')
|
|
114
|
+
|
|
115
|
+
# Erzeuge Variationen einer Wortform
|
|
116
|
+
variations = [obj.form]
|
|
117
|
+
@var_strings.each do |switch|
|
|
118
|
+
from, to = switch
|
|
119
|
+
variations = variate(variations, from, to)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Prüfe Variation auf bekanntes Wort
|
|
123
|
+
variations[0...@max_var].each do |var|
|
|
124
|
+
# Variiertes Wort im Wörterbuch suchen
|
|
125
|
+
word = @dic.find_word(var)
|
|
126
|
+
word = @gra.find_compositum(var) if word.unknown?
|
|
127
|
+
next if word.unknown? || (
|
|
128
|
+
word.attr == WA_KOMPOSITUM && word.lexicals.any? { |lex|
|
|
129
|
+
lex.attr[0..0] == LA_TAKEITASIS
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Das erste erkannte Wort beendet die Suche
|
|
134
|
+
inc('Anzahl gefundener Wörter')
|
|
135
|
+
word.form = @marker + var
|
|
136
|
+
forward(word)
|
|
137
|
+
return
|
|
138
|
+
end
|
|
144
139
|
end
|
|
145
|
-
end
|
|
146
140
|
|
|
147
|
-
|
|
148
|
-
|
|
141
|
+
forward(obj)
|
|
142
|
+
end
|
|
149
143
|
|
|
150
|
-
|
|
144
|
+
private
|
|
151
145
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
146
|
+
# Variiere die Bestandteile eines Arrays gemäß den Austauschvorgaben.
|
|
147
|
+
#
|
|
148
|
+
# variate( 'Tiieh', 'ieh', 'sch' ) => ['Tiieh', 'Tisch']
|
|
149
|
+
def variate(variation_list, from, to)
|
|
150
|
+
# neue Varianten sammeln
|
|
151
|
+
add_variations = []
|
|
152
|
+
from_re = Regexp.new(from)
|
|
159
153
|
|
|
160
|
-
|
|
161
|
-
|
|
154
|
+
# alle Wörter in der variation_list permutieren
|
|
155
|
+
variation_list.each do |wordform|
|
|
162
156
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
157
|
+
# Wortform in Teile zerlegen und anschließend Dimension feststellen
|
|
158
|
+
wordpart = " #{wordform} ".split( from_re )
|
|
159
|
+
n = wordpart.size - 1
|
|
166
160
|
|
|
167
|
-
|
|
168
|
-
|
|
161
|
+
# Austauschketten in Matrix hinterlegen
|
|
162
|
+
change = [from, to]
|
|
169
163
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
164
|
+
# Austauschketten auf alle Teile anwenden
|
|
165
|
+
(1..(2**n-1)).each do |i|
|
|
166
|
+
variation = wordpart[0]
|
|
167
|
+
# i[x] = Wert des x.ten Bit von Integer i
|
|
168
|
+
(1..n).each { |j| variation += change[i[j-1]] + wordpart[j] }
|
|
175
169
|
|
|
176
|
-
|
|
170
|
+
add_variations << variation.strip
|
|
171
|
+
end
|
|
177
172
|
end
|
|
173
|
+
|
|
174
|
+
variation_list + add_variations
|
|
178
175
|
end
|
|
179
176
|
|
|
180
|
-
variation_list + add_variations
|
|
181
177
|
end
|
|
182
178
|
|
|
183
179
|
end
|