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,103 +1,108 @@
|
|
|
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
|
-
# selektieren und weiterzuleiten. Im Prinzip werden alle erkannten Wörter gefiltert.
|
|
34
|
-
# Bei einem Indexierungslauf können so alle nicht durch den Wordsearcher erkannten Wörter,
|
|
35
|
-
# also die, die im Wörterbuch nicht enthalten sind, separat ausgegeben werden und als Grundlage für
|
|
36
|
-
# die Wörterbuchpflege dienen.
|
|
37
|
-
# Der Noneword_filter ist in einer frühen Entwicklungsphase entstanden. Die gleiche Funktion
|
|
38
|
-
# kann auch mit dem universelleren Objectfilter mit dem Ausdruck 'obj.kind_of?(Word) && obj.attr==WA_UNKNOWN'
|
|
39
|
-
# durchgeführt werden, mit dem einzigen Unterschied, dass der Noneword_filter nur die Wortform weiterleitet.
|
|
40
|
-
# Der Noneword_filter verschluckt ebenfalls alle Kommandos, ausser dem Dateianfang (*FILE) und Ende (*EOF),
|
|
41
|
-
# sowie dem LIR-Format-Spezifikum (*RECORD).
|
|
42
|
-
#
|
|
43
|
-
# *Hinweis* Dieser Attendee sammelt die auszugebenden Daten so lange, bis ein Dateiwechsel oder Record-Wechsel
|
|
44
|
-
# angekündigt wird. Erst dann werden alle Daten auf einmal weitergeleitet.
|
|
45
|
-
#
|
|
46
|
-
# === Mögliche Verlinkung
|
|
47
|
-
# Erwartet:: Daten vom Typ *Word*, z.B. von Abbreviator, Wordsearcher, Decomposer, Synonymer, Multiworder, Sequencer
|
|
48
|
-
# Erzeugt:: Daten vom Typ *String*, z.B. für Textwriter
|
|
49
|
-
#
|
|
50
|
-
# === Parameter
|
|
51
|
-
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
|
52
|
-
# Alle anderen Parameter müssen zwingend angegeben werden.
|
|
53
|
-
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
|
54
|
-
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
|
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
|
-
# - wordsearcher: { in: token, out: words, source: 'sys-dic' }
|
|
63
|
-
# - noneword_filter: { in: words, out: filtr }
|
|
64
|
-
# - debugger: { in: filtr, prompt: 'out>' }
|
|
65
|
-
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
|
66
|
-
# out> *FILE('test.txt')
|
|
67
|
-
# out> "lingo"
|
|
68
|
-
# out> *EOF('test.txt')
|
|
29
|
+
class Attendee
|
|
69
30
|
|
|
70
|
-
|
|
31
|
+
# Der NonewordFilter ermöglicht es, alle nicht erkannten Wörter aus dem Datenstrom zu
|
|
32
|
+
# selektieren und weiterzuleiten. Im Prinzip werden alle erkannten Wörter gefiltert.
|
|
33
|
+
# Bei einem Indexierungslauf können so alle nicht durch den Wordsearcher erkannten Wörter,
|
|
34
|
+
# also die, die im Wörterbuch nicht enthalten sind, separat ausgegeben werden und als Grundlage für
|
|
35
|
+
# die Wörterbuchpflege dienen.
|
|
36
|
+
# Der NonewordFilter ist in einer frühen Entwicklungsphase entstanden. Die gleiche Funktion
|
|
37
|
+
# kann auch mit dem universelleren Objectfilter mit dem Ausdruck 'obj.kind_of?(Word) && obj.attr==WA_UNKNOWN'
|
|
38
|
+
# durchgeführt werden, mit dem einzigen Unterschied, dass der NonewordFilter nur die Wortform weiterleitet.
|
|
39
|
+
# Der NonewordFilter verschluckt ebenfalls alle Kommandos, ausser dem Dateianfang (*FILE) und Ende (*EOF),
|
|
40
|
+
# sowie dem LIR-Format-Spezifikum (*RECORD).
|
|
41
|
+
#
|
|
42
|
+
# *Hinweis* Dieser Attendee sammelt die auszugebenden Daten so lange, bis ein Dateiwechsel oder Record-Wechsel
|
|
43
|
+
# angekündigt wird. Erst dann werden alle Daten auf einmal weitergeleitet.
|
|
44
|
+
#
|
|
45
|
+
# === Mögliche Verlinkung
|
|
46
|
+
# Erwartet:: Daten vom Typ *Word*, z.B. von Abbreviator, Wordsearcher, Decomposer, Synonymer, Multiworder, Sequencer
|
|
47
|
+
# Erzeugt:: Daten vom Typ *String*, z.B. für Textwriter
|
|
48
|
+
#
|
|
49
|
+
# === Parameter
|
|
50
|
+
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
|
51
|
+
# Alle anderen Parameter müssen zwingend angegeben werden.
|
|
52
|
+
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
|
53
|
+
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
|
54
|
+
#
|
|
55
|
+
# === Beispiele
|
|
56
|
+
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
|
57
|
+
# meeting:
|
|
58
|
+
# attendees:
|
|
59
|
+
# - text_reader: { out: lines, files: '$(files)' }
|
|
60
|
+
# - tokenizer: { in: lines, out: token }
|
|
61
|
+
# - word_searcher: { in: token, out: words, source: 'sys-dic' }
|
|
62
|
+
# - noneword_filter: { in: words, out: filtr }
|
|
63
|
+
# - debugger: { in: filtr, prompt: 'out>' }
|
|
64
|
+
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
|
65
|
+
# out> *FILE('test.txt')
|
|
66
|
+
# out> "lingo"
|
|
67
|
+
# out> *EOF('test.txt')
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
class NonewordFilter < self
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
@nonewords = []
|
|
76
|
-
end
|
|
71
|
+
protected
|
|
77
72
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
73
|
+
def init
|
|
74
|
+
@nonewords = []
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Control behandelt die Kommandos zum Öffnen und Schließen einer Datei.
|
|
78
|
+
# Für jede Datei wird ein neuer Satz nicht erkannter Wörter registriert.
|
|
79
|
+
def control(cmd, par)
|
|
80
|
+
case cmd
|
|
81
|
+
when STR_CMD_FILE
|
|
82
|
+
@nonewords.clear
|
|
83
|
+
when STR_CMD_EOL
|
|
84
|
+
skip_command
|
|
85
|
+
when STR_CMD_RECORD, STR_CMD_EOF
|
|
86
|
+
nones = @nonewords.sort.uniq
|
|
87
|
+
nones.each(&method(:forward))
|
|
88
|
+
add('Objekte gefiltert', nones.size)
|
|
89
|
+
@nonewords.clear
|
|
90
|
+
end
|
|
91
91
|
end
|
|
92
|
-
end
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
def process(obj)
|
|
94
|
+
if obj.is_a?(Word) && obj.unknown?
|
|
95
|
+
inc('Anzahl nicht erkannter Wörter')
|
|
96
|
+
@nonewords << obj.form.downcase
|
|
97
|
+
end
|
|
98
98
|
end
|
|
99
|
+
|
|
99
100
|
end
|
|
100
101
|
|
|
102
|
+
# For backwards compatibility.
|
|
103
|
+
Nonewordfilter = NonewordFilter
|
|
104
|
+
Noneword_filter = NonewordFilter
|
|
105
|
+
|
|
101
106
|
end
|
|
102
107
|
|
|
103
108
|
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
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
|
+
###############################################################################
|
|
25
|
+
#++
|
|
26
|
+
|
|
27
|
+
class Lingo
|
|
28
|
+
|
|
29
|
+
class Attendee
|
|
30
|
+
|
|
31
|
+
# Der ObjectFilter ermöglicht es, beliebige Objekte aus dem Datenstrom herauszufiltern.
|
|
32
|
+
# Um die gewünschten Objekte zu identifizieren, sind ein paar Ruby-Kenntnisse und das Wissen
|
|
33
|
+
# um die Lingo Klassen notwendig. Hier sollen kurz die häufigsten Fälle angesprochen werden:
|
|
34
|
+
#
|
|
35
|
+
# Filtern nach einem bestimmten Typ, z.B. Token oder Word wird beispielsweise durch den Ausdruck
|
|
36
|
+
# 'obj.kind_of?(Word)' ermöglicht. Token und Words haben jeweils ein Attribut +attr+.
|
|
37
|
+
# Bei Token gibt +attr+ an, mit welcher Tokenizer-Regel das Token erkannt wurde. So können z.B.
|
|
38
|
+
# alle numerischen Token mit dem Ausdruck 'obj.kind_of?(Token) && obj.attr=="NUMS"' identifiziert
|
|
39
|
+
# werden. Wie bereits gezeigt, können Bedingungen durch logisches UND (&&) oder ODER (||) verknüpft werden.
|
|
40
|
+
# Das Attribut +form+ kann genutzt werden, um auf den Text eines Objektes zuzugreifen, z.B.
|
|
41
|
+
# 'obj.form=="John"'.
|
|
42
|
+
#
|
|
43
|
+
# === Mögliche Verlinkung
|
|
44
|
+
# Erwartet:: Daten beliebigen Typs von allen Attendees
|
|
45
|
+
# Erzeugt:: Daten, die der als Parameter übergebenen Bedingung entsprechen
|
|
46
|
+
#
|
|
47
|
+
# === Parameter
|
|
48
|
+
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
|
49
|
+
# Alle anderen Parameter müssen zwingend angegeben werden.
|
|
50
|
+
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
|
51
|
+
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
|
52
|
+
# <b><i>objects</i></b>:: (Standard: true) Gibt einen Ruby-Ausdruck an, der, wenn der Ausdruck
|
|
53
|
+
# als Wahr ausgewertet wird, das Objekt weiterleitet und ansonsten filtert.
|
|
54
|
+
#
|
|
55
|
+
# === Beispiele
|
|
56
|
+
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
|
57
|
+
# meeting:
|
|
58
|
+
# attendees:
|
|
59
|
+
# - text_reader: { out: lines, files: '$(files)' }
|
|
60
|
+
# - tokenizer: { in: lines, out: token }
|
|
61
|
+
# - word_searcher: { in: token, out: words, source: 'sys-dic' }
|
|
62
|
+
# - object_filter: { in: words, out: filtr, objects: 'obj.kind_of?(Word) && obj.lexicals.size>0 && obj.lexicals[0].attr==LA_SUBSTANTIV' }
|
|
63
|
+
# - debugger: { in: filtr, prompt: 'out>' }
|
|
64
|
+
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
|
65
|
+
# out> *FILE('test.txt')
|
|
66
|
+
# out> <Indexierung = [(indexierung/s)]>
|
|
67
|
+
# out> <Indexierung = [(indexierung/s)]>
|
|
68
|
+
# out> *EOL('test.txt')
|
|
69
|
+
# out> *EOF('test.txt')
|
|
70
|
+
|
|
71
|
+
class ObjectFilter < self
|
|
72
|
+
|
|
73
|
+
protected
|
|
74
|
+
|
|
75
|
+
def init
|
|
76
|
+
@obj_eval = get_key('objects', 'true')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def process(obj)
|
|
80
|
+
forward(obj) if eval(@obj_eval)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# For backwards compatibility.
|
|
86
|
+
Objectfilter = ObjectFilter
|
|
87
|
+
Object_filter = ObjectFilter
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
@@ -1,188 +1,189 @@
|
|
|
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
|
-
class
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
29
|
+
class Attendee
|
|
30
|
+
|
|
31
|
+
# Der Sequencer ist von seiner Funktion her ähnlich dem Multiworder. Der Multiworder
|
|
32
|
+
# nutzt zur Erkennung von Mehrwortgruppen spezielle Wörterbücher, der Sequencer hingegen
|
|
33
|
+
# definierte Folgen von Wortklassen. Mit dem Sequencer können Indexterme generiert werden,
|
|
34
|
+
# die sich über mehrere Wörter erstrecken.
|
|
35
|
+
# Die Textfolge "automatische Indexierung und geniale Indexierung"
|
|
36
|
+
# wird bisher in die Indexterme "automatisch", "Indexierung" und "genial" zerlegt.
|
|
37
|
+
# Über die Konfiguration kann der Sequencer Mehrwortgruppen identifizieren, die
|
|
38
|
+
# z.B. aus einem Adjektiv und einem Substantiv bestehen. Mit der o.g. Textfolge würde
|
|
39
|
+
# dann auch "Indexierung, automatisch" und "Indexierung, genial" als Indexterm erzeugt
|
|
40
|
+
# werden. Welche Wortklassenfolgen erkannt werden sollen und wie die Ausgabe aussehen
|
|
41
|
+
# soll, wird dem Sequencer über seine Konfiguration mitgeteilt.
|
|
42
|
+
#
|
|
43
|
+
# === Mögliche Verlinkung
|
|
44
|
+
# Erwartet:: Daten vom Typ *Word* z.B. von Wordsearcher, Decomposer, Ocr_variator, Multiworder
|
|
45
|
+
# Erzeugt:: Daten vom Typ *Word* (mit Attribut WA_SEQUENCE). 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
|
|
46
|
+
#
|
|
47
|
+
# === Parameter
|
|
48
|
+
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
|
49
|
+
# Alle anderen Parameter müssen zwingend angegeben werden.
|
|
50
|
+
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
|
51
|
+
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
|
52
|
+
# <b><i>stopper</i></b>:: (Standard: TA_PUNCTUATION, TA_OTHER) Gibt die Begrenzungen an, zwischen
|
|
53
|
+
# denen der Sequencer suchen soll, i.d.R. Satzzeichen und Sonderzeichen,
|
|
54
|
+
# weil sie kaum in einer Mehrwortgruppen vorkommen.
|
|
55
|
+
#
|
|
56
|
+
# === Konfiguration
|
|
57
|
+
# Der Sequencer benötigt zur Identifikation von Mehrwortgruppen Regeln, nach denen er
|
|
58
|
+
# arbeiten soll. Die benötigten Regeln werden nicht als Parameter, sondern in der
|
|
59
|
+
# Sprachkonfiguration hinterlegt, die sich standardmäßig in der Datei
|
|
60
|
+
# <tt>de.lang</tt> befindet (YAML-Format).
|
|
61
|
+
# language:
|
|
62
|
+
# attendees:
|
|
63
|
+
# sequencer:
|
|
64
|
+
# sequences: [ [AS, "2, 1"], [AK, "2, 1"] ]
|
|
65
|
+
# Hiermit werden dem Sequencer zwei Regeln mitgeteilt: Er soll Adjektiv-Substantiv- (AS) und
|
|
66
|
+
# Adjektiv-Kompositum-Folgen (AK) erkennen. Zusätzlich ist angegeben, in welchem Format die
|
|
67
|
+
# dadurch ermittelte Wortfolge ausgegeben werden soll. In diesem Beispiel also zuerst das
|
|
68
|
+
# Substantiv und durch Komma getrennt das Adjektiv.
|
|
69
|
+
#
|
|
70
|
+
# === Beispiele
|
|
71
|
+
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
|
72
|
+
# meeting:
|
|
73
|
+
# attendees:
|
|
74
|
+
# - text_reader: { out: lines, files: '$(files)' }
|
|
75
|
+
# - tokenizer: { in: lines, out: token }
|
|
76
|
+
# - word_searcher: { in: token, out: words, source: 'sys-dic' }
|
|
77
|
+
# - sequencer: { in: words, out: seque }
|
|
78
|
+
# - debugger: { in: seque, prompt: 'out>' }
|
|
79
|
+
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
|
80
|
+
# out> *FILE('test.txt')
|
|
81
|
+
# out> <Lingo|?>
|
|
82
|
+
# out> <kann = [(koennen/v)]>
|
|
83
|
+
# out> <indexierung, automatisch|SEQ = [(indexierung, automatisch/q)]>
|
|
84
|
+
# out> <automatische = [(automatisch/a)]>
|
|
85
|
+
# out> <Indexierung = [(indexierung/s)]>
|
|
86
|
+
# out> <und = [(und/w)]>
|
|
87
|
+
# out> <indexierung, genial|SEQ = [(indexierung, genial/q)]>
|
|
88
|
+
# out> <geniale = [(genial/a), (genialisch/a)]>
|
|
89
|
+
# out> <Indexierung = [(indexierung/s)]>
|
|
90
|
+
# out> :./PUNC:
|
|
91
|
+
# out> *EOL('test.txt')
|
|
92
|
+
# out> *EOF('test.txt')
|
|
93
|
+
|
|
94
|
+
class Sequencer < BufferedAttendee
|
|
95
|
+
|
|
96
|
+
protected
|
|
97
|
+
|
|
98
|
+
def init
|
|
99
|
+
# Parameter verwerten
|
|
100
|
+
@stopper = get_array('stopper', TA_PUNCTUATION + ',' + TA_OTHER).map(&:upcase)
|
|
101
|
+
@seq_strings = get_key('sequences').map { |e| WordSequence.new(*e) }
|
|
102
|
+
|
|
103
|
+
raise MissingConfigError.new(:sequences) if @seq_strings.empty?
|
|
104
|
+
end
|
|
41
105
|
|
|
42
|
-
|
|
43
|
-
|
|
106
|
+
def control(cmd, par)
|
|
107
|
+
# Jedes Control-Object ist auch Auslöser der Verarbeitung
|
|
108
|
+
process_buffer if [STR_CMD_RECORD, STR_CMD_EOF].include?(cmd)
|
|
109
|
+
end
|
|
44
110
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
111
|
+
def process_buffer?
|
|
112
|
+
# start buffer processing when stopper token are found or at unknown words
|
|
113
|
+
item = @buffer.last
|
|
114
|
+
(item.is_a?(WordForm) && @stopper.include?(item.attr.upcase)) ||
|
|
115
|
+
(item.is_a?(Word) && item.unknown?)
|
|
48
116
|
end
|
|
49
|
-
end
|
|
50
117
|
|
|
51
|
-
|
|
118
|
+
def process_buffer
|
|
119
|
+
return if @buffer.empty?
|
|
52
120
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# definierte Folgen von Wortklassen. Mit dem Sequencer können Indexterme generiert werden,
|
|
56
|
-
# die sich über mehrere Wörter erstrecken.
|
|
57
|
-
# Die Textfolge "automatische Indexierung und geniale Indexierung"
|
|
58
|
-
# wird bisher in die Indexterme "automatisch", "Indexierung" und "genial" zerlegt.
|
|
59
|
-
# Über die Konfiguration kann der Sequencer Mehrwortgruppen identifizieren, die
|
|
60
|
-
# z.B. aus einem Adjektiv und einem Substantiv bestehen. Mit der o.g. Textfolge würde
|
|
61
|
-
# dann auch "Indexierung, automatisch" und "Indexierung, genial" als Indexterm erzeugt
|
|
62
|
-
# werden. Welche Wortklassenfolgen erkannt werden sollen und wie die Ausgabe aussehen
|
|
63
|
-
# soll, wird dem Sequencer über seine Konfiguration mitgeteilt.
|
|
64
|
-
#
|
|
65
|
-
# === Mögliche Verlinkung
|
|
66
|
-
# Erwartet:: Daten vom Typ *Word* z.B. von Wordsearcher, Decomposer, Ocr_variator, Multiworder
|
|
67
|
-
# Erzeugt:: Daten vom Typ *Word* (mit Attribut WA_SEQUENCE). 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
|
|
68
|
-
#
|
|
69
|
-
# === Parameter
|
|
70
|
-
# Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
|
|
71
|
-
# Alle anderen Parameter müssen zwingend angegeben werden.
|
|
72
|
-
# <b>in</b>:: siehe allgemeine Beschreibung des Attendee
|
|
73
|
-
# <b>out</b>:: siehe allgemeine Beschreibung des Attendee
|
|
74
|
-
# <b><i>stopper</i></b>:: (Standard: TA_PUNCTUATION, TA_OTHER) Gibt die Begrenzungen an, zwischen
|
|
75
|
-
# denen der Sequencer suchen soll, i.d.R. Satzzeichen und Sonderzeichen,
|
|
76
|
-
# weil sie kaum in einer Mehrwortgruppen vorkommen.
|
|
77
|
-
#
|
|
78
|
-
# === Konfiguration
|
|
79
|
-
# Der Sequencer benötigt zur Identifikation von Mehrwortgruppen Regeln, nach denen er
|
|
80
|
-
# arbeiten soll. Die benötigten Regeln werden nicht als Parameter, sondern in der
|
|
81
|
-
# Sprachkonfiguration hinterlegt, die sich standardmäßig in der Datei
|
|
82
|
-
# <tt>de.lang</tt> befindet (YAML-Format).
|
|
83
|
-
# language:
|
|
84
|
-
# attendees:
|
|
85
|
-
# sequencer:
|
|
86
|
-
# sequences: [ [AS, "2, 1"], [AK, "2, 1"] ]
|
|
87
|
-
# Hiermit werden dem Sequencer zwei Regeln mitgeteilt: Er soll Adjektiv-Substantiv- (AS) und
|
|
88
|
-
# Adjektiv-Kompositum-Folgen (AK) erkennen. Zusätzlich ist angegeben, in welchem Format die
|
|
89
|
-
# dadurch ermittelte Wortfolge ausgegeben werden soll. In diesem Beispiel also zuerst das
|
|
90
|
-
# Substantiv und durch Komma getrennt das Adjektiv.
|
|
91
|
-
#
|
|
92
|
-
# === Beispiele
|
|
93
|
-
# Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
|
|
94
|
-
# meeting:
|
|
95
|
-
# attendees:
|
|
96
|
-
# - textreader: { out: lines, files: '$(files)' }
|
|
97
|
-
# - tokenizer: { in: lines, out: token }
|
|
98
|
-
# - wordsearcher: { in: token, out: words, source: 'sys-dic' }
|
|
99
|
-
# - sequencer: { in: words, out: seque }
|
|
100
|
-
# - debugger: { in: seque, prompt: 'out>' }
|
|
101
|
-
# ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
|
|
102
|
-
# out> *FILE('test.txt')
|
|
103
|
-
# out> <Lingo|?>
|
|
104
|
-
# out> <kann = [(koennen/v)]>
|
|
105
|
-
# out> <indexierung, automatisch|SEQ = [(indexierung, automatisch/q)]>
|
|
106
|
-
# out> <automatische = [(automatisch/a)]>
|
|
107
|
-
# out> <Indexierung = [(indexierung/s)]>
|
|
108
|
-
# out> <und = [(und/w)]>
|
|
109
|
-
# out> <indexierung, genial|SEQ = [(indexierung, genial/q)]>
|
|
110
|
-
# out> <geniale = [(genial/a), (genialisch/a)]>
|
|
111
|
-
# out> <Indexierung = [(indexierung/s)]>
|
|
112
|
-
# out> :./PUNC:
|
|
113
|
-
# out> *EOL('test.txt')
|
|
114
|
-
# out> *EOF('test.txt')
|
|
115
|
-
|
|
116
|
-
class Attendee::Sequencer < BufferedAttendee
|
|
117
|
-
|
|
118
|
-
protected
|
|
119
|
-
|
|
120
|
-
def init
|
|
121
|
-
# Parameter verwerten
|
|
122
|
-
@stopper = get_array('stopper', TA_PUNCTUATION + ',' + TA_OTHER).map { |s| s.upcase }
|
|
123
|
-
@seq_strings = get_key('sequences').map { |e| WordSequence.new(*e) }
|
|
124
|
-
|
|
125
|
-
forward(STR_CMD_ERR, 'Konfiguration ist leer') if @seq_strings.empty?
|
|
126
|
-
end
|
|
121
|
+
unless @buffer.size < 2
|
|
122
|
+
matches = Hash.new { |h, k| h[k] = [] }
|
|
127
123
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
sequences(@buffer.map { |obj|
|
|
125
|
+
obj.is_a?(Word) && !obj.unknown? ? obj.attrs(false) : ['#']
|
|
126
|
+
}).uniq.each { |sequence|
|
|
127
|
+
@seq_strings.each { |wordseq|
|
|
128
|
+
wordseq.scan(sequence) { |pos, form, classes|
|
|
129
|
+
inc('Anzahl erkannter Sequenzen')
|
|
132
130
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
131
|
+
classes.each_with_index { |wc, index|
|
|
132
|
+
@buffer[pos + index].lexicals.find { |lex|
|
|
133
|
+
form.gsub!(index.succ.to_s, lex.form) if lex.attr == wc
|
|
134
|
+
} or break
|
|
135
|
+
} or next
|
|
136
|
+
|
|
137
|
+
matches[pos] << form
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
matches.sort.each { |pos, forms|
|
|
143
|
+
forms.uniq.each { |form|
|
|
144
|
+
deferred_insert(pos, Word.new_lexical(form, WA_SEQUENCE, LA_SEQUENCE))
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
end
|
|
142
148
|
|
|
143
|
-
|
|
144
|
-
|
|
149
|
+
forward_buffer
|
|
150
|
+
end
|
|
145
151
|
|
|
146
|
-
|
|
147
|
-
obj.is_a?(Word) && !obj.unknown? ? obj.attrs(false) : ['#']
|
|
148
|
-
}).uniq.each { |sequence|
|
|
149
|
-
@seq_strings.each { |wordseq|
|
|
150
|
-
wordseq.scan(sequence) { |pos, form, classes|
|
|
151
|
-
inc('Anzahl erkannter Sequenzen')
|
|
152
|
+
private
|
|
152
153
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
form.gsub!(index.succ.to_s, lex.form) if lex.attr == wc
|
|
156
|
-
} or break
|
|
157
|
-
} or next
|
|
154
|
+
def sequences(map)
|
|
155
|
+
res = map.shift
|
|
158
156
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
157
|
+
map.each { |classes|
|
|
158
|
+
temp = []
|
|
159
|
+
res.each { |wc1| classes.each { |wc2| temp << (wc1 + wc2) } }
|
|
160
|
+
res = temp
|
|
162
161
|
}
|
|
163
162
|
|
|
164
|
-
|
|
165
|
-
forms.uniq.each { |form|
|
|
166
|
-
deferred_insert(pos, Word.new_lexical(form, WA_SEQUENCE, LA_SEQUENCE))
|
|
167
|
-
}
|
|
168
|
-
}
|
|
163
|
+
res
|
|
169
164
|
end
|
|
170
165
|
|
|
171
|
-
|
|
172
|
-
end
|
|
166
|
+
class WordSequence
|
|
173
167
|
|
|
174
|
-
|
|
168
|
+
attr_reader :classes, :format, :string
|
|
175
169
|
|
|
176
|
-
|
|
177
|
-
|
|
170
|
+
def initialize(wordclasses, format)
|
|
171
|
+
@string = wordclasses.downcase
|
|
172
|
+
@classes = @string.split(//)
|
|
173
|
+
@format = format
|
|
174
|
+
end
|
|
178
175
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
176
|
+
def scan(sequence)
|
|
177
|
+
pos = 0
|
|
178
|
+
|
|
179
|
+
while pos = sequence.index(string, pos)
|
|
180
|
+
yield pos, format.dup, classes
|
|
181
|
+
pos += 1
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
end
|
|
184
186
|
|
|
185
|
-
res
|
|
186
187
|
end
|
|
187
188
|
|
|
188
189
|
end
|