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.
- data/.rspec +1 -0
- data/COPYING +663 -0
- data/ChangeLog +754 -0
- data/README +322 -0
- data/Rakefile +100 -0
- data/TODO +28 -0
- data/bin/lingo +5 -0
- data/bin/lingoctl +6 -0
- data/de.lang +121 -0
- data/de/lingo-abk.txt +74 -0
- data/de/lingo-dic.txt +56822 -0
- data/de/lingo-mul.txt +3209 -0
- data/de/lingo-syn.txt +14841 -0
- data/de/test_dic.txt +24 -0
- data/de/test_mul.txt +17 -0
- data/de/test_mul2.txt +2 -0
- data/de/test_singleword.txt +2 -0
- data/de/test_syn.txt +4 -0
- data/de/test_syn2.txt +1 -0
- data/de/user-dic.txt +10 -0
- data/en.lang +113 -0
- data/en/lingo-dic.txt +55434 -0
- data/en/lingo-mul.txt +456 -0
- data/en/user-dic.txt +5 -0
- data/info/Objekte.png +0 -0
- data/info/Typen.png +0 -0
- data/info/database.png +0 -0
- data/info/db_small.png +0 -0
- data/info/download.png +0 -0
- data/info/gpl-hdr.txt +27 -0
- data/info/kerze.png +0 -0
- data/info/language.png +0 -0
- data/info/lingo.png +0 -0
- data/info/logo.png +0 -0
- data/info/meeting.png +0 -0
- data/info/types.png +0 -0
- data/lib/lingo.rb +321 -0
- data/lib/lingo/attendee/abbreviator.rb +119 -0
- data/lib/lingo/attendee/debugger.rb +111 -0
- data/lib/lingo/attendee/decomposer.rb +101 -0
- data/lib/lingo/attendee/dehyphenizer.rb +167 -0
- data/lib/lingo/attendee/multiworder.rb +301 -0
- data/lib/lingo/attendee/noneword_filter.rb +103 -0
- data/lib/lingo/attendee/objectfilter.rb +86 -0
- data/lib/lingo/attendee/sequencer.rb +190 -0
- data/lib/lingo/attendee/synonymer.rb +105 -0
- data/lib/lingo/attendee/textreader.rb +237 -0
- data/lib/lingo/attendee/textwriter.rb +196 -0
- data/lib/lingo/attendee/tokenizer.rb +218 -0
- data/lib/lingo/attendee/variator.rb +185 -0
- data/lib/lingo/attendee/vector_filter.rb +158 -0
- data/lib/lingo/attendee/wordsearcher.rb +96 -0
- data/lib/lingo/attendees.rb +289 -0
- data/lib/lingo/cli.rb +62 -0
- data/lib/lingo/config.rb +104 -0
- data/lib/lingo/const.rb +131 -0
- data/lib/lingo/ctl.rb +173 -0
- data/lib/lingo/database.rb +587 -0
- data/lib/lingo/language.rb +530 -0
- data/lib/lingo/modules.rb +98 -0
- data/lib/lingo/types.rb +285 -0
- data/lib/lingo/utilities.rb +40 -0
- data/lib/lingo/version.rb +27 -0
- data/lingo-all.cfg +85 -0
- data/lingo-call.cfg +15 -0
- data/lingo.cfg +78 -0
- data/lingo.rb +3 -0
- data/lir.cfg +72 -0
- data/porter/stem.cfg +311 -0
- data/porter/stem.rb +150 -0
- data/spec/spec_helper.rb +0 -0
- data/test.cfg +79 -0
- data/test/attendee/ts_abbreviator.rb +35 -0
- data/test/attendee/ts_decomposer.rb +31 -0
- data/test/attendee/ts_multiworder.rb +390 -0
- data/test/attendee/ts_noneword_filter.rb +19 -0
- data/test/attendee/ts_objectfilter.rb +19 -0
- data/test/attendee/ts_sequencer.rb +43 -0
- data/test/attendee/ts_synonymer.rb +33 -0
- data/test/attendee/ts_textreader.rb +58 -0
- data/test/attendee/ts_textwriter.rb +98 -0
- data/test/attendee/ts_tokenizer.rb +32 -0
- data/test/attendee/ts_variator.rb +24 -0
- data/test/attendee/ts_vector_filter.rb +62 -0
- data/test/attendee/ts_wordsearcher.rb +119 -0
- data/test/lir.csv +3 -0
- data/test/lir.txt +12 -0
- data/test/lir2.txt +12 -0
- data/test/mul.txt +1 -0
- data/test/ref/artikel.mul +1 -0
- data/test/ref/artikel.non +159 -0
- data/test/ref/artikel.seq +270 -0
- data/test/ref/artikel.syn +16 -0
- data/test/ref/artikel.vec +928 -0
- data/test/ref/artikel.ven +928 -0
- data/test/ref/artikel.ver +928 -0
- data/test/ref/lir.csv +328 -0
- data/test/ref/lir.mul +1 -0
- data/test/ref/lir.non +274 -0
- data/test/ref/lir.seq +249 -0
- data/test/ref/lir.syn +94 -0
- data/test/test_helper.rb +113 -0
- data/test/ts_database.rb +269 -0
- data/test/ts_language.rb +396 -0
- data/txt/artikel-en.txt +157 -0
- data/txt/artikel.txt +170 -0
- data/txt/lir.txt +1317 -0
- 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
|