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
data/en/user-dic.txt ADDED
@@ -0,0 +1,5 @@
1
+ #
2
+ # Musterwörterbuch user-dic.txt als Vorlage
3
+ # für eigene Benutzerwörterbücher
4
+ #
5
+ #
data/info/Objekte.png ADDED
Binary file
data/info/Typen.png ADDED
Binary file
data/info/database.png ADDED
Binary file
data/info/db_small.png ADDED
Binary file
data/info/download.png ADDED
Binary file
data/info/gpl-hdr.txt ADDED
@@ -0,0 +1,27 @@
1
+ #--
2
+ # LINGO ist ein Indexierungssystem mit Grundformreduktion, Kompositumzerlegung,
3
+ # Mehrworterkennung und Relationierung.
4
+ #
5
+ # Copyright (C) 2005-2007 John Vorhauer
6
+ # Copyright (C) 2007-2011 John Vorhauer, Jens Wille
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify it under
9
+ # the terms of the GNU Affero General Public License as published by the Free
10
+ # Software Foundation; either version 3 of the License, or (at your option)
11
+ # any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful, but WITHOUT
14
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16
+ # details.
17
+ #
18
+ # You should have received a copy of the GNU Affero General Public License along
19
+ # with this program; if not, write to the Free Software Foundation, Inc.,
20
+ # 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
21
+ #
22
+ # For more information visit http://www.lex-lingo.de or contact me at
23
+ # welcomeATlex-lingoDOTde near 50°55'N+6°55'E.
24
+ #
25
+ # Lex Lingo rules from here on
26
+ #++
27
+
data/info/kerze.png ADDED
Binary file
data/info/language.png ADDED
Binary file
data/info/lingo.png ADDED
Binary file
data/info/logo.png ADDED
Binary file
data/info/meeting.png ADDED
Binary file
data/info/types.png ADDED
Binary file
data/lib/lingo.rb ADDED
@@ -0,0 +1,321 @@
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 'stringio'
31
+ require 'benchmark'
32
+ require 'nuggets/env/user_home'
33
+ require 'nuggets/numeric/duration'
34
+
35
+ require_relative 'lingo/config'
36
+ require_relative 'lingo/attendees'
37
+ require_relative 'lingo/attendee/abbreviator'
38
+ require_relative 'lingo/attendee/debugger'
39
+ require_relative 'lingo/attendee/decomposer'
40
+ require_relative 'lingo/attendee/dehyphenizer'
41
+ require_relative 'lingo/attendee/multiworder'
42
+ require_relative 'lingo/attendee/noneword_filter'
43
+ require_relative 'lingo/attendee/objectfilter'
44
+ require_relative 'lingo/attendee/variator'
45
+ require_relative 'lingo/attendee/sequencer'
46
+ require_relative 'lingo/attendee/synonymer'
47
+ require_relative 'lingo/attendee/textreader'
48
+ require_relative 'lingo/attendee/textwriter'
49
+ require_relative 'lingo/attendee/tokenizer'
50
+ require_relative 'lingo/attendee/vector_filter'
51
+ require_relative 'lingo/attendee/wordsearcher'
52
+ require_relative 'lingo/version'
53
+
54
+ class Lingo
55
+
56
+ # The system-wide Lingo directory (+LINGO_BASE+).
57
+ BASE = ENV['LINGO_BASE'] || File.expand_path('../..', __FILE__)
58
+
59
+ # The user's personal Lingo directory (+LINGO_HOME+).
60
+ HOME = ENV['LINGO_HOME'] || File.join(ENV.user_home, '.lingo')
61
+
62
+ # The local Lingo directory (+LINGO_CURR+).
63
+ CURR = ENV['LINGO_CURR'] || '.'
64
+
65
+ # The search path for Lingo dictionary and configuration files.
66
+ PATH = ENV['LINGO_PATH'] || [CURR, HOME, BASE].join(File::PATH_SEPARATOR)
67
+
68
+ FIND_OPTIONS = {
69
+ config: { dir: 'config', ext: 'cfg' },
70
+ dict: { dir: 'dict', ext: 'txt' },
71
+ lang: { dir: 'lang', ext: 'lang' },
72
+ store: { dir: 'store', ext: nil }
73
+ }
74
+
75
+ class << self
76
+
77
+ def talk(*args)
78
+ new(*args).talk
79
+ end
80
+
81
+ def call(cfg = find(:config, 'lingo-call'), args = [], &block)
82
+ Call.new(['-c', cfg, *args]).call(&block)
83
+ end
84
+
85
+ def error(msg)
86
+ abort(msg)
87
+ end
88
+
89
+ def list(type, options = {})
90
+ options = options_for(type, options)
91
+ path = path_for(options)
92
+
93
+ glob = file_with_ext('*', options)
94
+ glob = File.join('??', glob) if type == :dict
95
+
96
+ [].tap { |list| walk(path, options) { |dir|
97
+ Dir[File.join(dir, glob)].sort.each { |file|
98
+ pn = Pathname.new(file)
99
+ list << realpath_for(pn, path) if pn.file?
100
+ }
101
+ } }
102
+ end
103
+
104
+ def find(type, file, options = {})
105
+ if options.is_a?(Array)
106
+ path = options
107
+ options = options_for(type)
108
+ else
109
+ options = options_for(type, options)
110
+ path = path_for(options)
111
+ end
112
+
113
+ type = :file if type != :store
114
+ send("find_#{type}", file, path, options)
115
+ rescue RuntimeError, Errno::ENOENT => err
116
+ block_given? ? yield(err) : raise
117
+ end
118
+
119
+ def basename(type, file)
120
+ dir, name = File.split(file)
121
+ type != :dict ? name : File.join(File.basename(dir), name)
122
+ end
123
+
124
+ def basepath(type, file)
125
+ File.join(options_for(type)[:dir], basename(type, file))
126
+ end
127
+
128
+ private
129
+
130
+ def find_file(file, path, options)
131
+ pn = Pathname.new(file_with_ext(file, options)).cleanpath
132
+
133
+ walk(path, options) { |dir|
134
+ pn2 = pn.expand_path(dir)
135
+ pn = pn2 and break if pn2.exist?
136
+ } if pn.relative?
137
+
138
+ realpath_for(pn, path)
139
+ end
140
+
141
+ def find_store(file, path, options)
142
+ base = basename(:dict, find(:dict, file, path))
143
+
144
+ walk(path.reverse, options, false) { |dir|
145
+ Pathname.new(dir).ascend { |r|
146
+ break true if r.file?
147
+
148
+ return File.join(dir, base).tap { |s|
149
+ s.chomp!(File.extname(s))
150
+ } if r.writable?
151
+
152
+ break true if r.exist?
153
+ }
154
+ }
155
+
156
+ raise 'No writable store found in search path'
157
+ end
158
+
159
+ def options_for(type, options = {})
160
+ if find_options = FIND_OPTIONS[type]
161
+ options = find_options.merge(options)
162
+ else
163
+ raise ArgumentError, "Invalid type `#{type.inspect}'", caller(1)
164
+ end
165
+ end
166
+
167
+ def path_for(options)
168
+ options[:path] || PATH.split(File::PATH_SEPARATOR)
169
+ end
170
+
171
+ def file_with_ext(file, options)
172
+ ext = options[:ext]
173
+ ext && File.extname(file).empty? ? "#{file}.#{ext}" : file
174
+ end
175
+
176
+ def walk(path, options, legacy = true)
177
+ dirs = [options[:dir].to_s]
178
+ dirs << '' if legacy
179
+ dirs.uniq!
180
+
181
+ seen = Hash.new { |h, k| h[k] = true; false }
182
+
183
+ path.each { |d|
184
+ next if seen[d = File.expand_path(d)]
185
+ dirs.each { |i| yield File.join(d, i) } or break
186
+ }
187
+ end
188
+
189
+ def realpath_for(pn, path)
190
+ pn.realpath(path.first).to_s
191
+ end
192
+
193
+ end
194
+
195
+ attr_reader :dictionaries, :report_status, :report_time
196
+
197
+ def initialize(*args)
198
+ @config_args = args
199
+ reset(false)
200
+ end
201
+
202
+ def config
203
+ @config ||= Config.new(*@config_args)
204
+ end
205
+
206
+ def dictionary_config
207
+ @dictionary_config ||= config['language/dictionary']
208
+ end
209
+
210
+ def database_config(id)
211
+ dictionary_config['databases'][id]
212
+ end
213
+
214
+ def talk
215
+ invite
216
+ start
217
+ ensure
218
+ reset
219
+ end
220
+
221
+ def invite(list = config['meeting/attendees'])
222
+ supplier = Hash.new { |h, k| h[k] = [] }
223
+ subscriber = Hash.new { |h, k| h[k] = [] }
224
+
225
+ last_link, auto_link = '', 0
226
+
227
+ list.each { |hash|
228
+ # {'attendee' => {'name'=>'Attendee', 'in'=>'nase', 'out'=>'ohr', 'param'=>'hase'}}
229
+ cfg = hash.values.first.merge('name' => hash.keys.first.capitalize)
230
+
231
+ %w[in out].each { |key| (cfg[key] ||= '').downcase! }
232
+
233
+ cfg['in'] = last_link if cfg['in'].empty?
234
+ cfg['out'] = "auto_link_out_#{auto_link += 1}" if cfg['out'].empty?
235
+ last_link = cfg['out']
236
+
237
+ data = config["language/attendees/#{cfg['name'].downcase}"]
238
+ cfg.update(data) if data
239
+
240
+ attendee = Attendee.const_get(cfg['name']).new(cfg, self)
241
+ @attendees << attendee
242
+
243
+ cfg['in'].split(STRING_SEPERATOR_PATTERN).each { |interest|
244
+ subscriber[interest] << attendee
245
+ }
246
+ cfg['out'].split(STRING_SEPERATOR_PATTERN).each { |theme|
247
+ supplier[theme] << attendee
248
+ }
249
+ }
250
+
251
+ supplier.each { |channel, attendees| attendees.each { |att|
252
+ att.add_subscriber(subscriber[channel])
253
+ } }
254
+ end
255
+
256
+ def start(report_status = config['status'], report_time = config['perfmon'])
257
+ @report_status, @report_time = report_status, report_time
258
+
259
+ time = Benchmark.realtime {
260
+ @attendees.first.listen(AgendaItem.new(STR_CMD_TALK))
261
+ }
262
+
263
+ if report_status || report_time
264
+ config.stderr.puts "Require protocol...\n#{separator = '-' * 61}"
265
+ @attendees.first.listen(AgendaItem.new(STR_CMD_STATUS))
266
+ config.stderr.puts "#{separator}\nThe duration of the meeting was #{time.to_hms(2)}"
267
+ end
268
+ end
269
+
270
+ def reset(close = true)
271
+ dictionaries.each(&:close) if close
272
+ @dictionaries, @attendees = [], []
273
+ end
274
+
275
+ class Call < Lingo
276
+
277
+ def initialize(args = [])
278
+ super(args, StringIO.new, StringIO.new, StringIO.new)
279
+ end
280
+
281
+ def call
282
+ invite
283
+
284
+ if block_given?
285
+ begin
286
+ yield self
287
+ ensure
288
+ reset
289
+ end
290
+ else
291
+ self
292
+ end
293
+ end
294
+
295
+ def talk(str)
296
+ config.stdin.reopen(str)
297
+
298
+ start
299
+
300
+ %w[stdout stderr].flat_map { |key|
301
+ io = config.send(key).tap(&:rewind)
302
+ io.readlines.each(&:chomp!).tap {
303
+ io.truncate(0)
304
+ io.rewind
305
+ }
306
+ }.tap { |res|
307
+ if block_given?
308
+ res.map!(&Proc.new)
309
+ else
310
+ res.sort!
311
+ res.uniq!
312
+ end
313
+ }
314
+ end
315
+
316
+ end
317
+
318
+ end
319
+
320
+ require 'nuggets/util/pluggable'
321
+ Util::Pluggable.load_plugins_for(Lingo)
@@ -0,0 +1,119 @@
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 Erkennung von Abkürzungen kann auf vielfältige Weise erfolgen. In jedem Fall
33
+ # sollte eine sichere Unterscheidung von einem Satzende-Punkt möglich sein.
34
+ # Der in Lingo gewählte Ansatz befreit den Tokenizer von dieser Arbeit und konzentriert
35
+ # die Erkennung in diesem Attendee.
36
+ # Sobald der Abbreviator im Datenstrom auf ein Punkt trifft (Token = <tt>:./PUNC:</tt>),
37
+ # prüft er das vorhergehende Token auf eine gültige Abkürzung im Abkürzungs-Wörterbuch.
38
+ # Wird es als Abkürzung erkannt, dann wird das Token in ein Word gewandelt und das
39
+ # Punkt-Token aus dem Zeichenstrom entfernt.
40
+ #
41
+ # === Mögliche Verlinkung
42
+ # Erwartet:: Daten des Typs *Token* z.B. von Tokenizer
43
+ # Erzeugt:: Leitet Token weiter und wandelt erkannte Abkürzungen in den Typ *Word* z.B. für Wordsearcher
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
+ # - debugger: { in: abbrev, prompt: 'out>' }
61
+ # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
62
+ # out> *FILE('test.txt')
63
+ # out> :Dies/WORD:
64
+ # out> :ist/WORD:
65
+ # out> <ggf. = [(gegebenenfalls/w)]>
66
+ # out> :eine/WORD:
67
+ # out> :Abk³rzung/WORD:
68
+ # out> :./PUNC:
69
+ # out> *EOL('test.txt')
70
+ # out> *EOF('test.txt')
71
+
72
+ class Attendee::Abbreviator < BufferedAttendee
73
+
74
+ protected
75
+
76
+ def init
77
+ # Wörterbuch bereitstellen
78
+ src = get_array('source')
79
+ mod = get_key('mode', 'all')
80
+ @dic = Dictionary.new({'source'=>src, 'mode'=>mod}, @lingo)
81
+ end
82
+
83
+ def control(cmd, par)
84
+ @dic.report.each_pair { |key, value| set(key, value) } if cmd == STR_CMD_STATUS
85
+
86
+ # Jedes Control-Object ist auch Auslöser der Verarbeitung
87
+ process_buffer
88
+ end
89
+
90
+ private
91
+
92
+ def process_buffer?
93
+ @buffer[-1].kind_of?(Token) && @buffer[-1].form == CHAR_PUNCT
94
+ end
95
+
96
+ def process_buffer
97
+ if @buffer.size < 2
98
+ forward_buffer
99
+ return
100
+ end
101
+
102
+ # Wort vor dem Punkt im Abkürzungswörterbuch suchen
103
+ if @buffer[-2].kind_of?(Token)
104
+ inc('Anzahl gesuchter Abkürzungen')
105
+ abbr = @dic.find_word(@buffer[-2].form)
106
+ if abbr.attr == WA_IDENTIFIED
107
+ inc('Anzahl gefundener Abkürzungen')
108
+ abbr.form += CHAR_PUNCT
109
+ @buffer[-2] = abbr
110
+ @buffer.delete_at(-1)
111
+ end
112
+ end
113
+
114
+ forward_buffer
115
+ end
116
+
117
+ end
118
+
119
+ end