lingo 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/.rspec +1 -0
  2. data/COPYING +663 -0
  3. data/ChangeLog +754 -0
  4. data/README +322 -0
  5. data/Rakefile +100 -0
  6. data/TODO +28 -0
  7. data/bin/lingo +5 -0
  8. data/bin/lingoctl +6 -0
  9. data/de.lang +121 -0
  10. data/de/lingo-abk.txt +74 -0
  11. data/de/lingo-dic.txt +56822 -0
  12. data/de/lingo-mul.txt +3209 -0
  13. data/de/lingo-syn.txt +14841 -0
  14. data/de/test_dic.txt +24 -0
  15. data/de/test_mul.txt +17 -0
  16. data/de/test_mul2.txt +2 -0
  17. data/de/test_singleword.txt +2 -0
  18. data/de/test_syn.txt +4 -0
  19. data/de/test_syn2.txt +1 -0
  20. data/de/user-dic.txt +10 -0
  21. data/en.lang +113 -0
  22. data/en/lingo-dic.txt +55434 -0
  23. data/en/lingo-mul.txt +456 -0
  24. data/en/user-dic.txt +5 -0
  25. data/info/Objekte.png +0 -0
  26. data/info/Typen.png +0 -0
  27. data/info/database.png +0 -0
  28. data/info/db_small.png +0 -0
  29. data/info/download.png +0 -0
  30. data/info/gpl-hdr.txt +27 -0
  31. data/info/kerze.png +0 -0
  32. data/info/language.png +0 -0
  33. data/info/lingo.png +0 -0
  34. data/info/logo.png +0 -0
  35. data/info/meeting.png +0 -0
  36. data/info/types.png +0 -0
  37. data/lib/lingo.rb +321 -0
  38. data/lib/lingo/attendee/abbreviator.rb +119 -0
  39. data/lib/lingo/attendee/debugger.rb +111 -0
  40. data/lib/lingo/attendee/decomposer.rb +101 -0
  41. data/lib/lingo/attendee/dehyphenizer.rb +167 -0
  42. data/lib/lingo/attendee/multiworder.rb +301 -0
  43. data/lib/lingo/attendee/noneword_filter.rb +103 -0
  44. data/lib/lingo/attendee/objectfilter.rb +86 -0
  45. data/lib/lingo/attendee/sequencer.rb +190 -0
  46. data/lib/lingo/attendee/synonymer.rb +105 -0
  47. data/lib/lingo/attendee/textreader.rb +237 -0
  48. data/lib/lingo/attendee/textwriter.rb +196 -0
  49. data/lib/lingo/attendee/tokenizer.rb +218 -0
  50. data/lib/lingo/attendee/variator.rb +185 -0
  51. data/lib/lingo/attendee/vector_filter.rb +158 -0
  52. data/lib/lingo/attendee/wordsearcher.rb +96 -0
  53. data/lib/lingo/attendees.rb +289 -0
  54. data/lib/lingo/cli.rb +62 -0
  55. data/lib/lingo/config.rb +104 -0
  56. data/lib/lingo/const.rb +131 -0
  57. data/lib/lingo/ctl.rb +173 -0
  58. data/lib/lingo/database.rb +587 -0
  59. data/lib/lingo/language.rb +530 -0
  60. data/lib/lingo/modules.rb +98 -0
  61. data/lib/lingo/types.rb +285 -0
  62. data/lib/lingo/utilities.rb +40 -0
  63. data/lib/lingo/version.rb +27 -0
  64. data/lingo-all.cfg +85 -0
  65. data/lingo-call.cfg +15 -0
  66. data/lingo.cfg +78 -0
  67. data/lingo.rb +3 -0
  68. data/lir.cfg +72 -0
  69. data/porter/stem.cfg +311 -0
  70. data/porter/stem.rb +150 -0
  71. data/spec/spec_helper.rb +0 -0
  72. data/test.cfg +79 -0
  73. data/test/attendee/ts_abbreviator.rb +35 -0
  74. data/test/attendee/ts_decomposer.rb +31 -0
  75. data/test/attendee/ts_multiworder.rb +390 -0
  76. data/test/attendee/ts_noneword_filter.rb +19 -0
  77. data/test/attendee/ts_objectfilter.rb +19 -0
  78. data/test/attendee/ts_sequencer.rb +43 -0
  79. data/test/attendee/ts_synonymer.rb +33 -0
  80. data/test/attendee/ts_textreader.rb +58 -0
  81. data/test/attendee/ts_textwriter.rb +98 -0
  82. data/test/attendee/ts_tokenizer.rb +32 -0
  83. data/test/attendee/ts_variator.rb +24 -0
  84. data/test/attendee/ts_vector_filter.rb +62 -0
  85. data/test/attendee/ts_wordsearcher.rb +119 -0
  86. data/test/lir.csv +3 -0
  87. data/test/lir.txt +12 -0
  88. data/test/lir2.txt +12 -0
  89. data/test/mul.txt +1 -0
  90. data/test/ref/artikel.mul +1 -0
  91. data/test/ref/artikel.non +159 -0
  92. data/test/ref/artikel.seq +270 -0
  93. data/test/ref/artikel.syn +16 -0
  94. data/test/ref/artikel.vec +928 -0
  95. data/test/ref/artikel.ven +928 -0
  96. data/test/ref/artikel.ver +928 -0
  97. data/test/ref/lir.csv +328 -0
  98. data/test/ref/lir.mul +1 -0
  99. data/test/ref/lir.non +274 -0
  100. data/test/ref/lir.seq +249 -0
  101. data/test/ref/lir.syn +94 -0
  102. data/test/test_helper.rb +113 -0
  103. data/test/ts_database.rb +269 -0
  104. data/test/ts_language.rb +396 -0
  105. data/txt/artikel-en.txt +157 -0
  106. data/txt/artikel.txt +170 -0
  107. data/txt/lir.txt +1317 -0
  108. metadata +211 -0
@@ -0,0 +1,185 @@
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 Variator ermöglicht bei nicht erkannten Wörtern den listenbasierten
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
105
+
106
+ def control(cmd, par)
107
+ # Status wird abgefragt
108
+ if cmd == STR_CMD_STATUS
109
+ # Eigenen Status um Status von Dictionary und Grammer erweitern
110
+ @dic.report.each_pair { | k, v | set( k, v ) }
111
+ @gra.report.each_pair { | k, v | set( k, v ) }
112
+ end
113
+ end
114
+
115
+ def process(obj)
116
+ # Zu prüfende Wörter filtern
117
+ if obj.is_a?(Word) && @check[obj.attr]
118
+ # Statistik für Report
119
+ inc('Anzahl gesuchter Wörter')
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)
126
+ end
127
+
128
+ # Prüfe Variation auf bekanntes Wort
129
+ variations[0...@max_var].each do |var|
130
+ # Variiertes Wort im Wörterbuch suchen
131
+ word = @dic.find_word(var)
132
+ word = @gra.find_compositum(var) if word.attr == WA_UNKNOWN
133
+ next if word.attr == WA_UNKNOWN || (
134
+ word.attr == WA_KOMPOSITUM && word.lexicals.any? { |lex|
135
+ lex.attr[0..0] == LA_TAKEITASIS
136
+ }
137
+ )
138
+
139
+ # Das erste erkannte Wort beendet die Suche
140
+ inc('Anzahl gefundener Wörter')
141
+ word.form = @marker + var
142
+ forward(word)
143
+ return
144
+ end
145
+ end
146
+
147
+ forward(obj)
148
+ end
149
+
150
+ private
151
+
152
+ # Variiere die Bestandteile eines Arrays gemäß den Austauschvorgaben.
153
+ #
154
+ # variate( 'Tiieh', 'ieh', 'sch' ) => ['Tiieh', 'Tisch']
155
+ def variate(variation_list, from, to)
156
+ # neue Varianten sammeln
157
+ add_variations = []
158
+ from_re = Regexp.new(from)
159
+
160
+ # alle Wörter in der variation_list permutieren
161
+ variation_list.each do |wordform|
162
+
163
+ # Wortform in Teile zerlegen und anschließend Dimension feststellen
164
+ wordpart = " #{wordform} ".split( from_re )
165
+ n = wordpart.size - 1
166
+
167
+ # Austauschketten in Matrix hinterlegen
168
+ change = [from, to]
169
+
170
+ # Austauschketten auf alle Teile anwenden
171
+ (1..(2**n-1)).each do |i|
172
+ variation = wordpart[0]
173
+ # i[x] = Wert des x.ten Bit von Integer i
174
+ (1..n).each { |j| variation += change[i[j-1]] + wordpart[j] }
175
+
176
+ add_variations << variation.strip
177
+ end
178
+ end
179
+
180
+ variation_list + add_variations
181
+ end
182
+
183
+ end
184
+
185
+ end
@@ -0,0 +1,158 @@
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 Hauptaufgabe des Vector_filter ist die Erstellung eines Dokumenten-Index-Vektor.
33
+ # Dabei werden die durch die anderen Attendees ermittelten Grundformen eines Wortes
34
+ # gespeichert und bei einem Datei- oder Record-Wechsel weitergeleitet. Der Vector_filter
35
+ # kann bestimmte Wortklassen filtern und die Ergebnisse in verschiedenen Arten aufbereiten.
36
+ # Dabei werden Funktionen wie das einfache Zählen der Häufigkeit innerhalb eines Dokuments,
37
+ # aber auch die Term-Frequenz und unterschiedliche Ausgabeformate unterstützt.
38
+ #
39
+ # === Mögliche Verlinkung
40
+ # Erwartet:: Daten vom Typ *Word*, z.B. von Abbreviator, Wordsearcher, Decomposer, Synonymer, Multiworder, Sequencer
41
+ # Erzeugt:: Daten vom Typ *String*, z.B. für Textwriter
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><i>lexicals</i></b>:: (Standard: '[sy]' => die Wortklassen Substantiv und Synonyme werden gefiltert)
49
+ # Es können in eckige Klammern beliebige Wortklassen angegeben werden (siehe lib/strings.rb).
50
+ # Der Parameter wird als regulärer Ausdruck ausgewertet.
51
+ # <b><i>sort</i></b>:: (Standard: 'normal')
52
+ # Der Parameter +sort+ beeinflußt Verarbeitung und Ausgabeformat des Vector_filters.
53
+ # normal:: Jedes gefilterte Wort wird einmalig (keine Doppelnennungen!) in
54
+ # alphabetischer Reihenfolge in der Form "wort" ausgegeben.
55
+ # term_abs:: Jedes gefilterte Wort wird einmalig in absteigender Häufigkeit mit Angabe
56
+ # der absoluten Häufigkeit im Dokument in der Form "12 wort" ausgegeben.
57
+ # term_rel:: Jedes gefilterte Wort wird einmalig in absteigender Häufigkeit mit Angabe
58
+ # der relativen Häufigkeit im Dokument in der Form "0.1234 wort" ausgegeben.
59
+ # sto_abs:: Jedes gefilterte Wort wird einmalig in absteigender Häufigkeit mit Angabe
60
+ # der absoluten Häufigkeit im Dokument in der Form "wort {12}" ausgegeben.
61
+ # sto_rel:: Jedes gefilterte Wort wird einmalig in absteigender Häufigkeit mit Angabe
62
+ # der relativen Häufigkeit im Dokument in der Form "wort {0.1234}" ausgegeben.
63
+ # <b><i>skip</i></b>:: (Standard: TA_PUNCTUATION und TA_OTHER) Hiermit wird angegeben, welche Objekte nicht
64
+ # verarbeitet werden sollen. Die +skip+-Angabe bezieht sich auf das Attribut +attr+ von
65
+ # Token oder Word-Objekten.
66
+ #
67
+ # === Beispiele
68
+ # Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
69
+ # meeting:
70
+ # attendees:
71
+ # - textreader: { out: lines, files: '$(files)' }
72
+ # - tokenizer: { in: lines, out: token }
73
+ # - wordsearcher: { in: token, out: words, source: 'sys-dic' }
74
+ # - vector_filter: { in: words, out: filtr, sort: 'term_rel' }
75
+ # - debugger: { in: filtr, prompt: 'out>' }
76
+ # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
77
+ # out> *FILE('test.txt')
78
+ # out> "0.28571 indexierung"
79
+ # out> *EOF('test.txt')
80
+
81
+ class Attendee::Vector_filter < Attendee
82
+
83
+ protected
84
+
85
+ def init
86
+ @lexis = Regexp.new(get_key('lexicals', '[sy]').downcase)
87
+ @sort = get_key('sort', 'normal').downcase
88
+ @skip = get_array('skip', TA_PUNCTUATION+','+TA_OTHER).collect {|s| s.upcase }
89
+ @vectors = Array.new
90
+ @word_count = 0
91
+
92
+ if @debug = get_key('debug', false)
93
+ @prompt = get_key('prompt', 'lex:) ')
94
+ end
95
+ end
96
+
97
+ def control(cmd, par)
98
+ case cmd
99
+ when STR_CMD_EOL
100
+ deleteCmd
101
+ when STR_CMD_FILE, STR_CMD_RECORD, STR_CMD_EOF
102
+ @debug ? @vectors.each { |str| forward(str) } : sendVector
103
+ @vectors.clear
104
+ end
105
+ end
106
+
107
+ def process(obj)
108
+ if @debug
109
+ @vectors << "#{@prompt} #{obj.inspect}" if eval(@debug)
110
+ elsif obj.is_a?(Word)
111
+ @word_count += 1 if @skip.index(obj.attr).nil?
112
+ unless obj.lexicals.nil?
113
+ lexis = obj.get_class(@lexis) #lexicals.collect { |lex| (lex.attr =~ @lexis) ? lex : nil }.compact # get_class(@lexis)
114
+ lexis.each { |lex| @vectors << lex.form.downcase }
115
+ add('Anzahl von Vektor-Wörtern', lexis.size)
116
+ end
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ def sendVector
123
+ return if @vectors.size==0
124
+
125
+ add('Objekte gefiltert', @vectors.size)
126
+
127
+ # Array der Vector-Wörter zählen und nach Häufigkeit sortieren
128
+ if @sort=='normal'
129
+ @vectors = @vectors.compact.sort.uniq
130
+ else
131
+ cnt = Hash.new(0)
132
+ @vectors.compact.each { |e| cnt[e]+=1 }
133
+ @vectors = cnt.to_a.sort { |x,y|
134
+ if (y[1]<=>x[1])==0
135
+ x[0]<=>y[0]
136
+ else
137
+ y[1]<=>x[1]
138
+ end
139
+ }
140
+ end
141
+
142
+ # Vectoren je nach Parameter formatiert weiterleiten
143
+ @vectors.collect { |vec|
144
+ case @sort
145
+ when 'term_abs' then sprintf "%d %s", vec[1], vec[0]
146
+ when 'term_rel' then sprintf "%6.5f %s", vec[1].to_f/@word_count, vec[0]
147
+ when 'sto_abs' then sprintf "%s {%d}", vec[0], vec[1]
148
+ when 'sto_rel' then sprintf "%s {%6.5f}", vec[0], vec[1].to_f/@word_count
149
+ else sprintf "%s", vec
150
+ end
151
+ }.each { |str| forward(str) }
152
+
153
+ @word_count = 0 if @sort == 'sto_rel'
154
+ end
155
+
156
+ end
157
+
158
+ end
@@ -0,0 +1,96 @@
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 Wordsearcher ist das Herzstück von Lingo. Er macht die Hauptarbeit und versucht
33
+ # alle Token die nach einem sinnvollen Wort aussehen, in den ihm angegebenen
34
+ # Wörterbüchern zu finden und aufzulösen. Dabei werden die im Wörterbuch gefundenen
35
+ # Grundformen inkl. Wortklassen an das Word-Objekt angehängt.
36
+ #
37
+ # === Mögliche Verlinkung
38
+ # Erwartet:: Daten vom Typ *Token* (andere werden einfach durchgereicht) z.B. von Tokenizer, Abbreviator
39
+ # Erzeugt:: Daten vom Typ *Word* für erkannte Wörter z.B. für Synonymer, Decomposer, Ocr_variator, Multiworder, Sequencer, Noneword_filter, Vector_filter
40
+ #
41
+ # === Parameter
42
+ # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
43
+ # Alle anderen Parameter müssen zwingend angegeben werden.
44
+ # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
45
+ # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
46
+ # <b>source</b>:: siehe allgemeine Beschreibung des Dictionary
47
+ # <b><i>mode</i></b>:: (Standard: all) siehe allgemeine Beschreibung des Dictionary
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
+ # - debugger: { in: words, prompt: 'out>' }
58
+ # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
59
+ # out> *FILE('test.txt')
60
+ # out> <Dies = [(dies/w)]>
61
+ # out> <ist = [(sein/v)]>
62
+ # out> <ggf. = [(gegebenenfalls/w)]>
63
+ # out> <eine = [(einen/v), (ein/w)]>
64
+ # out> <Abk³rzung = [(abk³rzung/s)]>
65
+ # out> :./PUNC:
66
+ # out> *EOL('test.txt')
67
+ # out> *EOF('test.txt')
68
+
69
+ class Attendee::Wordsearcher < Attendee
70
+
71
+ def init
72
+ # Wörterbuch bereitstellen
73
+ src = get_array('source')
74
+ mod = get_key('mode', 'all')
75
+ @dic = Dictionary.new({'source'=>src, 'mode'=>mod}, @lingo)
76
+ end
77
+
78
+ def control(cmd, par)
79
+ @dic.report.each_pair { |key, value|
80
+ set(key, value)
81
+ } if cmd == STR_CMD_STATUS
82
+ end
83
+
84
+ def process(obj)
85
+ if obj.is_a?(Token) && obj.attr == TA_WORD
86
+ inc('Anzahl gesuchter Wörter')
87
+ word = @dic.find_word(obj.form)
88
+ inc('Anzahl gefundener Wörter') unless word.attr == WA_UNKNOWN
89
+ obj = word
90
+ end
91
+ forward(obj)
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,289 @@
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
+ require_relative 'modules'
31
+ require_relative 'language'
32
+ require_relative 'const'
33
+ require_relative 'types'
34
+
35
+ class Lingo
36
+
37
+ # Lingo ist als universelles Indexierungssystem entworfen worden. Seine Stärke liegt in der einfachen Konfigurierbarkeit für
38
+ # spezifische Aufgaben und in der schnelle Entwicklung weiterer Funktionen durch systematischen Kapselung der Komplexität auf
39
+ # kleine Verarbeitungseinheiten. Die kleinste Verarbeitungseinheit wird Attendee genannt. Um ein gewünschtes Verarbeitungsergebnis
40
+ # zu bekommen, werden die benötigten Attendees einfach in einer Reihe hinter einander geschaltet. Ein einfaches Beispiel hierfür ist
41
+ # eine direkte Verbindung zwischen einem Textreader, einem Tokenizer und einem Textwriter. Alle drei Klassen sind von der Klasse
42
+ # Attendee abgeleitet.
43
+ #
44
+ # Der Textreader liest beispielsweise Zeilen aus einer Textdatei und leitet sie weiter an den Tokenizer. Der Tokenizer zerlegt eine
45
+ # Textzeile in einzelne Wörter und gibt diese weiter an den Textwriter, der diese in eine (andere) Datei schreibt. Über vielfältige
46
+ # Konfigurationsmöglichkeiten kann das Verhalten der Attendees an die eigenen Bedürfnisse angepasst werden.
47
+ #
48
+ # Die Verkettung einzelner Attendees findet über die Schnittstellen +listen+ und +talk+ statt. An +listen+ können beliebige Objekte
49
+ # zur Ver- und Bearbeitung übergeben werden. Nach der Verarbeitung werden sie mittels +talk+ an die verketteten Attendees weiter
50
+ # gegeben. Objekte der Klasse AgendaItem dienen dabei der Steuerung der Verarbeitung und sind nicht Bestandteil der normalen
51
+ # Verarbeitung. Beispiele für AgendaItems sind die Kommandos TALK (Aufforderung zum Start der Verarbeitung), WARN (zur Ausgabe von
52
+ # Warnungen eines Attendees) und EOL (End of Line, Ende einer Textzeile nach Zerlegung in einzelne Wörter). Eine vollständige
53
+ # Übersicht benutzer AgendaItems (oder auf Stream Commands) steht in lib/const.rb mit dem Prefix STR_CMD_.
54
+ #
55
+ # Um die Entwicklung von neuen Attendees zu beschleunigen, wird durch die Vererbung sind bei wird die gesammte sind in der Regel nur
56
+ # drei abstrakte Methoden zu implementieren: +init+, +control+ und +process+. Die Methode +init+ wird bei der Instanziierung eines
57
+ # Objektes einmalig aufgerufen. Sie dient der Vorbereitung der Verarbeitung, z.B. durch das Öffnen und Bereitstellen von
58
+ # Wörterbüchern zur linguistischen Analyse. An die Methode +control+ werden alle eingehenden AgendaItems weitergeleitet. Dort erfolgt
59
+ # die Verarbeitungssteuerung, also z.B. bei STR_CMD_FILE das Öffnen einer Datei und bei STR_CMD_EOF respektive das Schließen. Die
60
+ # echte Verarbeitung von Daten findet daher durch die Methode +process+ statt.
61
+ #
62
+ # was macht attendee
63
+ # - verkettung der attendees anhand von konfigurationsinformationen
64
+ # - bereitstellung von globalen und spezifischen konfigurationsinformationen
65
+ # - behandlung von bestimmten übergreifenden Kommandos, z.B. STR_CMD_TALK, STR_CMD_STATUS, STR_CMD_WARN, STR_CMD_ERR
66
+ # - separierung und routing von kommando bzw. datenobjekten
67
+ #
68
+ # was macht die abgeleitet klasse
69
+ # - verarbeitet und/oder transformiert datenobjekte
70
+ # - wird gesteuert durch kommandos
71
+ # - schreibt verarbeitungsstatistiken
72
+
73
+ class Attendee
74
+
75
+ include Reportable
76
+
77
+ def initialize(config, lingo)
78
+ @lingo = lingo
79
+
80
+ init_reportable
81
+
82
+ begin
83
+ lingo.dictionary_config
84
+ rescue
85
+ raise "Fehler in der .lang-Datei bei 'language/dictionary'"
86
+ end
87
+
88
+ @config, @subscriber = config, []
89
+
90
+ init if self.class.method_defined?(:init)
91
+
92
+ @attendee_can_control = self.class.method_defined?(:control)
93
+ @attendee_can_process = self.class.method_defined?(:process)
94
+
95
+ @skip_this_command, @start_of_processing = false, nil
96
+ end
97
+
98
+ def add_subscriber(subscriber)
99
+ @subscriber.concat(subscriber)
100
+ end
101
+
102
+ def listen(obj)
103
+ unless obj.is_a?(AgendaItem)
104
+ if @attendee_can_process
105
+ inc(STA_NUM_OBJECTS)
106
+
107
+ unless @lingo.report_time
108
+ process(obj)
109
+ else
110
+ @start_of_processing = Time.new
111
+ process(obj)
112
+ add(STA_TIM_OBJECTS, Time.new - @start_of_processing)
113
+ end
114
+ else
115
+ forward(obj)
116
+ end
117
+ else
118
+ # Neuen TOP verarbeiten
119
+ if @attendee_can_control
120
+ inc(STA_NUM_COMMANDS)
121
+
122
+ unless @lingo.report_time
123
+ control(obj.cmd, obj.param)
124
+ else
125
+ @start_of_processing = Time.new
126
+ control(obj.cmd, obj.param)
127
+ add(STA_TIM_COMMANDS, Time.new - @start_of_processing)
128
+ end
129
+ end
130
+
131
+ # Spezialbehandlung für einige TOPs nach Verarbeitung
132
+ case obj.cmd
133
+ when STR_CMD_TALK
134
+ # keine weitere Behandlung oder Weiterleitung
135
+ nil
136
+ when STR_CMD_STATUS
137
+ # Standardprotokollinformationen ausgeben
138
+
139
+ if @lingo.report_time
140
+ @lingo.config.stderr.puts 'Perf: %-15s => %7d commands in %s (%s/cmd), %8d objects in %s (%s/obj)' % [
141
+ @config['name'],
142
+ get(STA_NUM_COMMANDS),
143
+ seconds_to_str(get(STA_TIM_COMMANDS)),
144
+ seconds_to_str((get(STA_NUM_COMMANDS)==0) ? 0.0 : get(STA_TIM_COMMANDS) / get(STA_NUM_COMMANDS).to_f),
145
+ get(STA_NUM_OBJECTS),
146
+ seconds_to_str(get(STA_TIM_OBJECTS)),
147
+ seconds_to_str((get(STA_NUM_OBJECTS)==0) ? 0.0 : get(STA_TIM_OBJECTS) / get(STA_NUM_OBJECTS).to_f)
148
+ ]
149
+ end
150
+
151
+ if @lingo.report_status
152
+ @lingo.config.stderr.puts "Attendee <%s> was connected from '%s' to '%s' reporting...\n" % @config.values_at(*%w[name in out]),
153
+ report.sort.map { |info| " #{info[0]} = #{info[1]}" }, nil
154
+ end
155
+
156
+ forward(obj.cmd, obj.param)
157
+ else
158
+ if @skip_this_command
159
+ @skip_this_command = false
160
+ else
161
+ forward(obj.cmd, obj.param)
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ def talk(obj)
168
+ time_for_sub = Time.new if @lingo.report_time
169
+ @subscriber.each { |attendee| attendee.listen(obj) }
170
+ @start_of_processing += (Time.new - time_for_sub) if @lingo.report_time
171
+ end
172
+
173
+ private
174
+
175
+ # ---------------------------------------------------
176
+ # Create intelligent output of performance times
177
+ # measured with command line option -p
178
+ # ---------------------------------------------------
179
+
180
+ def seconds_to_str(float)
181
+ if float < 0.001
182
+ "%9.3f µs" % [float * 1000000.0]
183
+ elsif float < 1.0
184
+ "%9.3f ms" % [float * 1000.0]
185
+ elsif float < 60.0
186
+ "%9.3f s " % [float]
187
+ elsif float < 3600.0
188
+ "%9.3f m " % [float / 60.0]
189
+ else
190
+ "%9.3f h " % [float / 3600.0]
191
+ end
192
+ end
193
+
194
+ def deleteCmd
195
+ @skip_this_command = true
196
+ end
197
+
198
+ def forward(obj, param=nil)
199
+ if param.nil?
200
+ # Information weiterreichen
201
+ talk(obj)
202
+ else
203
+ # TOP weiterreichen (wenn keine Warnung oder Fehler)
204
+ case obj
205
+ when STR_CMD_WARN then printf "+%s\n| %s: %s\n+%s\n", '-'*60, @config['name'], param, '-'*60
206
+ when STR_CMD_ERR then printf "%s\n= %s: %s\n%s\n", '='*61, @config['name'], param, '='*61; exit( 1 )
207
+ else
208
+ talk(AgendaItem.new(obj, param))
209
+ end
210
+ end
211
+ end
212
+
213
+ # ---------------------------------------------------
214
+ # Konfigurationshilfsmethoden
215
+ # ---------------------------------------------------
216
+ def has_key?(key)
217
+ !@config.nil? && @config.has_key?(key)
218
+ end
219
+
220
+ def get_key(key, default=nil)
221
+ forward(STR_CMD_ERR, "Attribut #{key} nicht gesetzt") if default.nil? && !has_key?(key)
222
+ @config.fetch(key, default)
223
+ end
224
+
225
+ def get_array(key, default=nil)
226
+ get_key(key, default).split(STRING_SEPERATOR_PATTERN)
227
+ end
228
+
229
+ # ---------------------------------------------------
230
+ # Abstrakte Methoden
231
+ #
232
+ # init
233
+ # control(cmd, param)
234
+ # process(obj)
235
+ # ---------------------------------------------------
236
+
237
+ end
238
+
239
+ class BufferedAttendee < Attendee
240
+
241
+ BufferInsert = Struct.new(:position, :object)
242
+
243
+ def initialize(config, lingo)
244
+ # In den Buffer werden alle Objekte geschrieben, bis process_buffer? == true ist
245
+ @buffer = []
246
+
247
+ # deferred_inserts beeinflussen nicht die Buffer-Größe, sondern werden an einer
248
+ # bestimmten Stelle in den Datenstrom eingefügt
249
+ @deferred_inserts = []
250
+
251
+ super
252
+ end
253
+
254
+ protected
255
+
256
+ def process(obj)
257
+ @buffer.push(obj)
258
+ process_buffer if process_buffer?
259
+ end
260
+
261
+ private
262
+
263
+ def forward_buffer
264
+ # Aufgeschobene Einfügungen in Buffer kopieren
265
+ @deferred_inserts.sort_by { |ins| ins.position }.each { |ins|
266
+ @buffer.insert(ins.position, ins.object)
267
+ }
268
+ @deferred_inserts.clear
269
+
270
+ # Buffer weiterleiten
271
+ @buffer.each { |obj| forward(obj) }
272
+ @buffer.clear
273
+ end
274
+
275
+ def process_buffer?
276
+ true
277
+ end
278
+
279
+ def process_buffer
280
+ # to be defined by child class
281
+ end
282
+
283
+ def deferred_insert(pos, obj)
284
+ @deferred_inserts << BufferInsert.new(pos, obj)
285
+ end
286
+
287
+ end
288
+
289
+ end