lingo 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. data/.rspec +1 -0
  2. data/COPYING +663 -0
  3. data/ChangeLog +754 -0
  4. data/README +322 -0
  5. data/Rakefile +100 -0
  6. data/TODO +28 -0
  7. data/bin/lingo +5 -0
  8. data/bin/lingoctl +6 -0
  9. data/de.lang +121 -0
  10. data/de/lingo-abk.txt +74 -0
  11. data/de/lingo-dic.txt +56822 -0
  12. data/de/lingo-mul.txt +3209 -0
  13. data/de/lingo-syn.txt +14841 -0
  14. data/de/test_dic.txt +24 -0
  15. data/de/test_mul.txt +17 -0
  16. data/de/test_mul2.txt +2 -0
  17. data/de/test_singleword.txt +2 -0
  18. data/de/test_syn.txt +4 -0
  19. data/de/test_syn2.txt +1 -0
  20. data/de/user-dic.txt +10 -0
  21. data/en.lang +113 -0
  22. data/en/lingo-dic.txt +55434 -0
  23. data/en/lingo-mul.txt +456 -0
  24. data/en/user-dic.txt +5 -0
  25. data/info/Objekte.png +0 -0
  26. data/info/Typen.png +0 -0
  27. data/info/database.png +0 -0
  28. data/info/db_small.png +0 -0
  29. data/info/download.png +0 -0
  30. data/info/gpl-hdr.txt +27 -0
  31. data/info/kerze.png +0 -0
  32. data/info/language.png +0 -0
  33. data/info/lingo.png +0 -0
  34. data/info/logo.png +0 -0
  35. data/info/meeting.png +0 -0
  36. data/info/types.png +0 -0
  37. data/lib/lingo.rb +321 -0
  38. data/lib/lingo/attendee/abbreviator.rb +119 -0
  39. data/lib/lingo/attendee/debugger.rb +111 -0
  40. data/lib/lingo/attendee/decomposer.rb +101 -0
  41. data/lib/lingo/attendee/dehyphenizer.rb +167 -0
  42. data/lib/lingo/attendee/multiworder.rb +301 -0
  43. data/lib/lingo/attendee/noneword_filter.rb +103 -0
  44. data/lib/lingo/attendee/objectfilter.rb +86 -0
  45. data/lib/lingo/attendee/sequencer.rb +190 -0
  46. data/lib/lingo/attendee/synonymer.rb +105 -0
  47. data/lib/lingo/attendee/textreader.rb +237 -0
  48. data/lib/lingo/attendee/textwriter.rb +196 -0
  49. data/lib/lingo/attendee/tokenizer.rb +218 -0
  50. data/lib/lingo/attendee/variator.rb +185 -0
  51. data/lib/lingo/attendee/vector_filter.rb +158 -0
  52. data/lib/lingo/attendee/wordsearcher.rb +96 -0
  53. data/lib/lingo/attendees.rb +289 -0
  54. data/lib/lingo/cli.rb +62 -0
  55. data/lib/lingo/config.rb +104 -0
  56. data/lib/lingo/const.rb +131 -0
  57. data/lib/lingo/ctl.rb +173 -0
  58. data/lib/lingo/database.rb +587 -0
  59. data/lib/lingo/language.rb +530 -0
  60. data/lib/lingo/modules.rb +98 -0
  61. data/lib/lingo/types.rb +285 -0
  62. data/lib/lingo/utilities.rb +40 -0
  63. data/lib/lingo/version.rb +27 -0
  64. data/lingo-all.cfg +85 -0
  65. data/lingo-call.cfg +15 -0
  66. data/lingo.cfg +78 -0
  67. data/lingo.rb +3 -0
  68. data/lir.cfg +72 -0
  69. data/porter/stem.cfg +311 -0
  70. data/porter/stem.rb +150 -0
  71. data/spec/spec_helper.rb +0 -0
  72. data/test.cfg +79 -0
  73. data/test/attendee/ts_abbreviator.rb +35 -0
  74. data/test/attendee/ts_decomposer.rb +31 -0
  75. data/test/attendee/ts_multiworder.rb +390 -0
  76. data/test/attendee/ts_noneword_filter.rb +19 -0
  77. data/test/attendee/ts_objectfilter.rb +19 -0
  78. data/test/attendee/ts_sequencer.rb +43 -0
  79. data/test/attendee/ts_synonymer.rb +33 -0
  80. data/test/attendee/ts_textreader.rb +58 -0
  81. data/test/attendee/ts_textwriter.rb +98 -0
  82. data/test/attendee/ts_tokenizer.rb +32 -0
  83. data/test/attendee/ts_variator.rb +24 -0
  84. data/test/attendee/ts_vector_filter.rb +62 -0
  85. data/test/attendee/ts_wordsearcher.rb +119 -0
  86. data/test/lir.csv +3 -0
  87. data/test/lir.txt +12 -0
  88. data/test/lir2.txt +12 -0
  89. data/test/mul.txt +1 -0
  90. data/test/ref/artikel.mul +1 -0
  91. data/test/ref/artikel.non +159 -0
  92. data/test/ref/artikel.seq +270 -0
  93. data/test/ref/artikel.syn +16 -0
  94. data/test/ref/artikel.vec +928 -0
  95. data/test/ref/artikel.ven +928 -0
  96. data/test/ref/artikel.ver +928 -0
  97. data/test/ref/lir.csv +328 -0
  98. data/test/ref/lir.mul +1 -0
  99. data/test/ref/lir.non +274 -0
  100. data/test/ref/lir.seq +249 -0
  101. data/test/ref/lir.syn +94 -0
  102. data/test/test_helper.rb +113 -0
  103. data/test/ts_database.rb +269 -0
  104. data/test/ts_language.rb +396 -0
  105. data/txt/artikel-en.txt +157 -0
  106. data/txt/artikel.txt +170 -0
  107. data/txt/lir.txt +1317 -0
  108. metadata +211 -0
@@ -0,0 +1,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