lingo 1.8.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/ChangeLog +13 -0
  2. data/README +49 -29
  3. data/Rakefile +28 -4
  4. data/TODO +2 -9
  5. data/bin/lingo +24 -0
  6. data/bin/lingoctl +24 -0
  7. data/de/lingo-dic.txt +559 -74
  8. data/info/gpl-hdr.txt +21 -24
  9. data/lib/lingo.rb +83 -112
  10. data/lib/lingo/agenda_item.rb +53 -0
  11. data/lib/lingo/attendee.rb +261 -0
  12. data/lib/lingo/attendee/abbreviator.rb +95 -97
  13. data/lib/lingo/attendee/debugger.rb +94 -93
  14. data/lib/lingo/attendee/decomposer.rb +76 -83
  15. data/lib/lingo/attendee/dehyphenizer.rb +141 -144
  16. data/lib/lingo/attendee/formatter.rb +65 -0
  17. data/lib/lingo/attendee/multi_worder.rb +302 -0
  18. data/lib/lingo/attendee/noneword_filter.rb +89 -84
  19. data/lib/lingo/attendee/object_filter.rb +91 -0
  20. data/lib/lingo/attendee/sequencer.rb +159 -158
  21. data/lib/lingo/attendee/synonymer.rb +81 -84
  22. data/lib/lingo/attendee/text_reader.rb +242 -0
  23. data/lib/lingo/attendee/text_writer.rb +169 -0
  24. data/lib/lingo/attendee/tokenizer.rb +192 -191
  25. data/lib/lingo/attendee/variator.rb +152 -156
  26. data/lib/lingo/attendee/vector_filter.rb +140 -135
  27. data/lib/lingo/attendee/word_searcher.rb +98 -0
  28. data/lib/lingo/buffered_attendee.rb +69 -0
  29. data/lib/lingo/cachable.rb +58 -0
  30. data/lib/lingo/call.rb +72 -0
  31. data/lib/lingo/cli.rb +26 -0
  32. data/lib/lingo/config.rb +23 -26
  33. data/lib/lingo/core_ext.rb +42 -0
  34. data/lib/lingo/ctl.rb +239 -173
  35. data/lib/lingo/database.rb +148 -496
  36. data/lib/lingo/database/crypter.rb +85 -0
  37. data/lib/lingo/database/gdbm_store.rb +49 -0
  38. data/lib/lingo/database/hash_store.rb +67 -0
  39. data/lib/lingo/database/libcdb_store.rb +58 -0
  40. data/lib/lingo/database/sdbm_store.rb +64 -0
  41. data/lib/lingo/database/show_progress.rb +81 -0
  42. data/lib/lingo/database/source.rb +134 -0
  43. data/lib/lingo/database/source/key_value.rb +62 -0
  44. data/lib/lingo/database/source/multi_key.rb +65 -0
  45. data/lib/lingo/database/source/multi_value.rb +65 -0
  46. data/lib/lingo/database/source/single_word.rb +60 -0
  47. data/lib/lingo/database/source/word_class.rb +64 -0
  48. data/lib/lingo/error.rb +122 -0
  49. data/lib/lingo/language.rb +78 -518
  50. data/lib/lingo/language/dictionary.rb +173 -0
  51. data/lib/lingo/language/grammar.rb +211 -0
  52. data/lib/lingo/language/lexical.rb +66 -0
  53. data/lib/lingo/language/lexical_hash.rb +88 -0
  54. data/lib/lingo/language/token.rb +48 -0
  55. data/lib/lingo/language/word.rb +130 -0
  56. data/lib/lingo/language/word_form.rb +83 -0
  57. data/lib/lingo/reportable.rb +59 -0
  58. data/lib/lingo/version.rb +1 -1
  59. data/lingo-all.cfg +14 -10
  60. data/lingo-call.cfg +5 -5
  61. data/lingo.cfg +14 -12
  62. data/lingo.rb +26 -0
  63. data/lir.cfg +13 -9
  64. data/spec/spec_helper.rb +1 -0
  65. data/test.cfg +11 -11
  66. data/test/attendee/ts_abbreviator.rb +0 -6
  67. data/test/attendee/ts_decomposer.rb +0 -6
  68. data/test/attendee/{ts_multiworder.rb → ts_multi_worder.rb} +1 -7
  69. data/test/attendee/ts_noneword_filter.rb +1 -7
  70. data/test/attendee/{ts_objectfilter.rb → ts_object_filter.rb} +1 -7
  71. data/test/attendee/ts_sequencer.rb +0 -6
  72. data/test/attendee/ts_synonymer.rb +0 -6
  73. data/test/attendee/{ts_textreader.rb → ts_text_reader.rb} +1 -7
  74. data/test/attendee/{ts_textwriter.rb → ts_text_writer.rb} +1 -7
  75. data/test/attendee/ts_tokenizer.rb +0 -6
  76. data/test/attendee/ts_variator.rb +0 -6
  77. data/test/attendee/ts_vector_filter.rb +1 -7
  78. data/test/attendee/{ts_wordsearcher.rb → ts_word_searcher.rb} +1 -7
  79. data/test/ref/artikel.non +2 -29
  80. data/test/ref/artikel.seq +13 -8
  81. data/test/ref/artikel.vec +30 -15
  82. data/test/ref/artikel.ven +29 -14
  83. data/test/ref/artikel.ver +58 -43
  84. data/test/ref/lir.csv +146 -145
  85. data/test/ref/lir.non +186 -210
  86. data/test/ref/lir.seq +54 -50
  87. data/test/test_helper.rb +41 -36
  88. data/test/ts_database.rb +12 -11
  89. data/test/ts_language.rb +118 -68
  90. metadata +67 -29
  91. data/lib/lingo/attendee/multiworder.rb +0 -301
  92. data/lib/lingo/attendee/objectfilter.rb +0 -86
  93. data/lib/lingo/attendee/textreader.rb +0 -237
  94. data/lib/lingo/attendee/textwriter.rb +0 -196
  95. data/lib/lingo/attendee/wordsearcher.rb +0 -96
  96. data/lib/lingo/attendees.rb +0 -289
  97. data/lib/lingo/const.rb +0 -131
  98. data/lib/lingo/modules.rb +0 -98
  99. data/lib/lingo/types.rb +0 -285
  100. data/lib/lingo/utilities.rb +0 -40
@@ -1,103 +1,108 @@
1
1
  # encoding: utf-8
2
2
 
3
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
4
+ ###############################################################################
5
+ # #
6
+ # Lingo -- A full-featured automatic indexing system #
7
+ # #
8
+ # Copyright (C) 2005-2007 John Vorhauer #
9
+ # Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
10
+ # #
11
+ # Lingo is free software; you can redistribute it and/or modify it under the #
12
+ # terms of the GNU Affero General Public License as published by the Free #
13
+ # Software Foundation; either version 3 of the License, or (at your option) #
14
+ # any later version. #
15
+ # #
16
+ # Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
17
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
18
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
19
+ # more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
28
25
  #++
29
26
 
30
27
  class Lingo
31
28
 
32
- # Der Noneword_filter ermöglicht es, alle nicht erkannten Wörter aus dem Datenstrom zu
33
- # selektieren und weiterzuleiten. Im Prinzip werden alle erkannten Wörter gefiltert.
34
- # Bei einem Indexierungslauf können so alle nicht durch den Wordsearcher erkannten Wörter,
35
- # also die, die im Wörterbuch nicht enthalten sind, separat ausgegeben werden und als Grundlage für
36
- # die Wörterbuchpflege dienen.
37
- # Der Noneword_filter ist in einer frühen Entwicklungsphase entstanden. Die gleiche Funktion
38
- # kann auch mit dem universelleren Objectfilter mit dem Ausdruck 'obj.kind_of?(Word) && obj.attr==WA_UNKNOWN'
39
- # durchgeführt werden, mit dem einzigen Unterschied, dass der Noneword_filter nur die Wortform weiterleitet.
40
- # Der Noneword_filter verschluckt ebenfalls alle Kommandos, ausser dem Dateianfang (*FILE) und Ende (*EOF),
41
- # sowie dem LIR-Format-Spezifikum (*RECORD).
42
- #
43
- # *Hinweis* Dieser Attendee sammelt die auszugebenden Daten so lange, bis ein Dateiwechsel oder Record-Wechsel
44
- # angekündigt wird. Erst dann werden alle Daten auf einmal weitergeleitet.
45
- #
46
- # === Mögliche Verlinkung
47
- # Erwartet:: Daten vom Typ *Word*, z.B. von Abbreviator, Wordsearcher, Decomposer, Synonymer, Multiworder, Sequencer
48
- # Erzeugt:: Daten vom Typ *String*, z.B. für Textwriter
49
- #
50
- # === Parameter
51
- # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
52
- # Alle anderen Parameter müssen zwingend angegeben werden.
53
- # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
54
- # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
55
- #
56
- # === Beispiele
57
- # Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
58
- # meeting:
59
- # attendees:
60
- # - textreader: { out: lines, files: '$(files)' }
61
- # - tokenizer: { in: lines, out: token }
62
- # - wordsearcher: { in: token, out: words, source: 'sys-dic' }
63
- # - noneword_filter: { in: words, out: filtr }
64
- # - debugger: { in: filtr, prompt: 'out>' }
65
- # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
66
- # out> *FILE('test.txt')
67
- # out> "lingo"
68
- # out> *EOF('test.txt')
29
+ class Attendee
69
30
 
70
- class Attendee::Noneword_filter < Attendee
31
+ # Der NonewordFilter ermöglicht es, alle nicht erkannten Wörter aus dem Datenstrom zu
32
+ # selektieren und weiterzuleiten. Im Prinzip werden alle erkannten Wörter gefiltert.
33
+ # Bei einem Indexierungslauf können so alle nicht durch den Wordsearcher erkannten Wörter,
34
+ # also die, die im Wörterbuch nicht enthalten sind, separat ausgegeben werden und als Grundlage für
35
+ # die Wörterbuchpflege dienen.
36
+ # Der NonewordFilter ist in einer frühen Entwicklungsphase entstanden. Die gleiche Funktion
37
+ # kann auch mit dem universelleren Objectfilter mit dem Ausdruck 'obj.kind_of?(Word) && obj.attr==WA_UNKNOWN'
38
+ # durchgeführt werden, mit dem einzigen Unterschied, dass der NonewordFilter nur die Wortform weiterleitet.
39
+ # Der NonewordFilter verschluckt ebenfalls alle Kommandos, ausser dem Dateianfang (*FILE) und Ende (*EOF),
40
+ # sowie dem LIR-Format-Spezifikum (*RECORD).
41
+ #
42
+ # *Hinweis* Dieser Attendee sammelt die auszugebenden Daten so lange, bis ein Dateiwechsel oder Record-Wechsel
43
+ # angekündigt wird. Erst dann werden alle Daten auf einmal weitergeleitet.
44
+ #
45
+ # === Mögliche Verlinkung
46
+ # Erwartet:: Daten vom Typ *Word*, z.B. von Abbreviator, Wordsearcher, Decomposer, Synonymer, Multiworder, Sequencer
47
+ # Erzeugt:: Daten vom Typ *String*, z.B. für Textwriter
48
+ #
49
+ # === Parameter
50
+ # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
51
+ # Alle anderen Parameter müssen zwingend angegeben werden.
52
+ # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
53
+ # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
54
+ #
55
+ # === Beispiele
56
+ # Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
57
+ # meeting:
58
+ # attendees:
59
+ # - text_reader: { out: lines, files: '$(files)' }
60
+ # - tokenizer: { in: lines, out: token }
61
+ # - word_searcher: { in: token, out: words, source: 'sys-dic' }
62
+ # - noneword_filter: { in: words, out: filtr }
63
+ # - debugger: { in: filtr, prompt: 'out>' }
64
+ # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
65
+ # out> *FILE('test.txt')
66
+ # out> "lingo"
67
+ # out> *EOF('test.txt')
71
68
 
72
- protected
69
+ class NonewordFilter < self
73
70
 
74
- def init
75
- @nonewords = []
76
- end
71
+ protected
77
72
 
78
- # Control behandelt die Kommandos zum Öffnen und Schließen einer Datei.
79
- # Für jede Datei wird ein neuer Satz nicht erkannter Wörter registriert.
80
- def control(cmd, par)
81
- case cmd
82
- when STR_CMD_FILE
83
- @nonewords.clear
84
- when STR_CMD_EOL
85
- deleteCmd
86
- when STR_CMD_RECORD, STR_CMD_EOF
87
- nones = @nonewords.sort.uniq
88
- nones.each { |nw| forward(nw) }
89
- add('Objekte gefiltert', nones.size)
90
- @nonewords.clear
73
+ def init
74
+ @nonewords = []
75
+ end
76
+
77
+ # Control behandelt die Kommandos zum Öffnen und Schließen einer Datei.
78
+ # Für jede Datei wird ein neuer Satz nicht erkannter Wörter registriert.
79
+ def control(cmd, par)
80
+ case cmd
81
+ when STR_CMD_FILE
82
+ @nonewords.clear
83
+ when STR_CMD_EOL
84
+ skip_command
85
+ when STR_CMD_RECORD, STR_CMD_EOF
86
+ nones = @nonewords.sort.uniq
87
+ nones.each(&method(:forward))
88
+ add('Objekte gefiltert', nones.size)
89
+ @nonewords.clear
90
+ end
91
91
  end
92
- end
93
92
 
94
- def process(obj)
95
- if obj.is_a?(Word) && obj.attr==WA_UNKNOWN
96
- inc('Anzahl nicht erkannter Wörter')
97
- @nonewords << obj.form.downcase
93
+ def process(obj)
94
+ if obj.is_a?(Word) && obj.unknown?
95
+ inc('Anzahl nicht erkannter Wörter')
96
+ @nonewords << obj.form.downcase
97
+ end
98
98
  end
99
+
99
100
  end
100
101
 
102
+ # For backwards compatibility.
103
+ Nonewordfilter = NonewordFilter
104
+ Noneword_filter = NonewordFilter
105
+
101
106
  end
102
107
 
103
108
  end
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ ###############################################################################
5
+ # #
6
+ # Lingo -- A full-featured automatic indexing system #
7
+ # #
8
+ # Copyright (C) 2005-2007 John Vorhauer #
9
+ # Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
10
+ # #
11
+ # Lingo is free software; you can redistribute it and/or modify it under the #
12
+ # terms of the GNU Affero General Public License as published by the Free #
13
+ # Software Foundation; either version 3 of the License, or (at your option) #
14
+ # any later version. #
15
+ # #
16
+ # Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
17
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
18
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
19
+ # more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
25
+ #++
26
+
27
+ class Lingo
28
+
29
+ class Attendee
30
+
31
+ # Der ObjectFilter ermöglicht es, beliebige Objekte aus dem Datenstrom herauszufiltern.
32
+ # Um die gewünschten Objekte zu identifizieren, sind ein paar Ruby-Kenntnisse und das Wissen
33
+ # um die Lingo Klassen notwendig. Hier sollen kurz die häufigsten Fälle angesprochen werden:
34
+ #
35
+ # Filtern nach einem bestimmten Typ, z.B. Token oder Word wird beispielsweise durch den Ausdruck
36
+ # 'obj.kind_of?(Word)' ermöglicht. Token und Words haben jeweils ein Attribut +attr+.
37
+ # Bei Token gibt +attr+ an, mit welcher Tokenizer-Regel das Token erkannt wurde. So können z.B.
38
+ # alle numerischen Token mit dem Ausdruck 'obj.kind_of?(Token) && obj.attr=="NUMS"' identifiziert
39
+ # werden. Wie bereits gezeigt, können Bedingungen durch logisches UND (&&) oder ODER (||) verknüpft werden.
40
+ # Das Attribut +form+ kann genutzt werden, um auf den Text eines Objektes zuzugreifen, z.B.
41
+ # 'obj.form=="John"'.
42
+ #
43
+ # === Mögliche Verlinkung
44
+ # Erwartet:: Daten beliebigen Typs von allen Attendees
45
+ # Erzeugt:: Daten, die der als Parameter übergebenen Bedingung entsprechen
46
+ #
47
+ # === Parameter
48
+ # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
49
+ # Alle anderen Parameter müssen zwingend angegeben werden.
50
+ # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
51
+ # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
52
+ # <b><i>objects</i></b>:: (Standard: true) Gibt einen Ruby-Ausdruck an, der, wenn der Ausdruck
53
+ # als Wahr ausgewertet wird, das Objekt weiterleitet und ansonsten filtert.
54
+ #
55
+ # === Beispiele
56
+ # Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
57
+ # meeting:
58
+ # attendees:
59
+ # - text_reader: { out: lines, files: '$(files)' }
60
+ # - tokenizer: { in: lines, out: token }
61
+ # - word_searcher: { in: token, out: words, source: 'sys-dic' }
62
+ # - object_filter: { in: words, out: filtr, objects: 'obj.kind_of?(Word) && obj.lexicals.size>0 && obj.lexicals[0].attr==LA_SUBSTANTIV' }
63
+ # - debugger: { in: filtr, prompt: 'out>' }
64
+ # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
65
+ # out> *FILE('test.txt')
66
+ # out> <Indexierung = [(indexierung/s)]>
67
+ # out> <Indexierung = [(indexierung/s)]>
68
+ # out> *EOL('test.txt')
69
+ # out> *EOF('test.txt')
70
+
71
+ class ObjectFilter < self
72
+
73
+ protected
74
+
75
+ def init
76
+ @obj_eval = get_key('objects', 'true')
77
+ end
78
+
79
+ def process(obj)
80
+ forward(obj) if eval(@obj_eval)
81
+ end
82
+
83
+ end
84
+
85
+ # For backwards compatibility.
86
+ Objectfilter = ObjectFilter
87
+ Object_filter = ObjectFilter
88
+
89
+ end
90
+
91
+ end
@@ -1,188 +1,189 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  #--
4
- # 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
4
+ ###############################################################################
5
+ # #
6
+ # Lingo -- A full-featured automatic indexing system #
7
+ # #
8
+ # Copyright (C) 2005-2007 John Vorhauer #
9
+ # Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
10
+ # #
11
+ # Lingo is free software; you can redistribute it and/or modify it under the #
12
+ # terms of the GNU Affero General Public License as published by the Free #
13
+ # Software Foundation; either version 3 of the License, or (at your option) #
14
+ # any later version. #
15
+ # #
16
+ # Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
17
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
18
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
19
+ # more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
28
25
  #++
29
26
 
30
27
  class Lingo
31
28
 
32
- class WordSequence
33
-
34
- attr_reader :classes, :format, :string
35
-
36
- def initialize(wordclasses, format)
37
- @string = wordclasses.downcase
38
- @classes = @string.split(//)
39
- @format = format
40
- end
29
+ class Attendee
30
+
31
+ # Der Sequencer ist von seiner Funktion her ähnlich dem Multiworder. Der Multiworder
32
+ # nutzt zur Erkennung von Mehrwortgruppen spezielle Wörterbücher, der Sequencer hingegen
33
+ # definierte Folgen von Wortklassen. Mit dem Sequencer können Indexterme generiert werden,
34
+ # die sich über mehrere Wörter erstrecken.
35
+ # Die Textfolge "automatische Indexierung und geniale Indexierung"
36
+ # wird bisher in die Indexterme "automatisch", "Indexierung" und "genial" zerlegt.
37
+ # Über die Konfiguration kann der Sequencer Mehrwortgruppen identifizieren, die
38
+ # z.B. aus einem Adjektiv und einem Substantiv bestehen. Mit der o.g. Textfolge würde
39
+ # dann auch "Indexierung, automatisch" und "Indexierung, genial" als Indexterm erzeugt
40
+ # werden. Welche Wortklassenfolgen erkannt werden sollen und wie die Ausgabe aussehen
41
+ # soll, wird dem Sequencer über seine Konfiguration mitgeteilt.
42
+ #
43
+ # === Mögliche Verlinkung
44
+ # Erwartet:: Daten vom Typ *Word* z.B. von Wordsearcher, Decomposer, Ocr_variator, Multiworder
45
+ # Erzeugt:: Daten vom Typ *Word* (mit Attribut WA_SEQUENCE). Je erkannter Mehrwortgruppe wird ein zusätzliches Word-Objekt in den Datenstrom eingefügt. Z.B. für Ocr_variator, Sequencer, Noneword_filter, Vector_filter
46
+ #
47
+ # === Parameter
48
+ # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
49
+ # Alle anderen Parameter müssen zwingend angegeben werden.
50
+ # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
51
+ # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
52
+ # <b><i>stopper</i></b>:: (Standard: TA_PUNCTUATION, TA_OTHER) Gibt die Begrenzungen an, zwischen
53
+ # denen der Sequencer suchen soll, i.d.R. Satzzeichen und Sonderzeichen,
54
+ # weil sie kaum in einer Mehrwortgruppen vorkommen.
55
+ #
56
+ # === Konfiguration
57
+ # Der Sequencer benötigt zur Identifikation von Mehrwortgruppen Regeln, nach denen er
58
+ # arbeiten soll. Die benötigten Regeln werden nicht als Parameter, sondern in der
59
+ # Sprachkonfiguration hinterlegt, die sich standardmäßig in der Datei
60
+ # <tt>de.lang</tt> befindet (YAML-Format).
61
+ # language:
62
+ # attendees:
63
+ # sequencer:
64
+ # sequences: [ [AS, "2, 1"], [AK, "2, 1"] ]
65
+ # Hiermit werden dem Sequencer zwei Regeln mitgeteilt: Er soll Adjektiv-Substantiv- (AS) und
66
+ # Adjektiv-Kompositum-Folgen (AK) erkennen. Zusätzlich ist angegeben, in welchem Format die
67
+ # dadurch ermittelte Wortfolge ausgegeben werden soll. In diesem Beispiel also zuerst das
68
+ # Substantiv und durch Komma getrennt das Adjektiv.
69
+ #
70
+ # === Beispiele
71
+ # Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
72
+ # meeting:
73
+ # attendees:
74
+ # - text_reader: { out: lines, files: '$(files)' }
75
+ # - tokenizer: { in: lines, out: token }
76
+ # - word_searcher: { in: token, out: words, source: 'sys-dic' }
77
+ # - sequencer: { in: words, out: seque }
78
+ # - debugger: { in: seque, prompt: 'out>' }
79
+ # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
80
+ # out> *FILE('test.txt')
81
+ # out> <Lingo|?>
82
+ # out> <kann = [(koennen/v)]>
83
+ # out> <indexierung, automatisch|SEQ = [(indexierung, automatisch/q)]>
84
+ # out> <automatische = [(automatisch/a)]>
85
+ # out> <Indexierung = [(indexierung/s)]>
86
+ # out> <und = [(und/w)]>
87
+ # out> <indexierung, genial|SEQ = [(indexierung, genial/q)]>
88
+ # out> <geniale = [(genial/a), (genialisch/a)]>
89
+ # out> <Indexierung = [(indexierung/s)]>
90
+ # out> :./PUNC:
91
+ # out> *EOL('test.txt')
92
+ # out> *EOF('test.txt')
93
+
94
+ class Sequencer < BufferedAttendee
95
+
96
+ protected
97
+
98
+ def init
99
+ # Parameter verwerten
100
+ @stopper = get_array('stopper', TA_PUNCTUATION + ',' + TA_OTHER).map(&:upcase)
101
+ @seq_strings = get_key('sequences').map { |e| WordSequence.new(*e) }
102
+
103
+ raise MissingConfigError.new(:sequences) if @seq_strings.empty?
104
+ end
41
105
 
42
- def scan(sequence)
43
- pos = 0
106
+ def control(cmd, par)
107
+ # Jedes Control-Object ist auch Auslöser der Verarbeitung
108
+ process_buffer if [STR_CMD_RECORD, STR_CMD_EOF].include?(cmd)
109
+ end
44
110
 
45
- while pos = sequence.index(string, pos)
46
- yield pos, format.dup, classes
47
- pos += 1
111
+ def process_buffer?
112
+ # start buffer processing when stopper token are found or at unknown words
113
+ item = @buffer.last
114
+ (item.is_a?(WordForm) && @stopper.include?(item.attr.upcase)) ||
115
+ (item.is_a?(Word) && item.unknown?)
48
116
  end
49
- end
50
117
 
51
- end
118
+ def process_buffer
119
+ return if @buffer.empty?
52
120
 
53
- # Der Sequencer ist von seiner Funktion her ähnlich dem Multiworder. Der Multiworder
54
- # nutzt zur Erkennung von Mehrwortgruppen spezielle Wörterbücher, der Sequencer hingegen
55
- # definierte Folgen von Wortklassen. Mit dem Sequencer können Indexterme generiert werden,
56
- # die sich über mehrere Wörter erstrecken.
57
- # Die Textfolge "automatische Indexierung und geniale Indexierung"
58
- # wird bisher in die Indexterme "automatisch", "Indexierung" und "genial" zerlegt.
59
- # Über die Konfiguration kann der Sequencer Mehrwortgruppen identifizieren, die
60
- # z.B. aus einem Adjektiv und einem Substantiv bestehen. Mit der o.g. Textfolge würde
61
- # dann auch "Indexierung, automatisch" und "Indexierung, genial" als Indexterm erzeugt
62
- # werden. Welche Wortklassenfolgen erkannt werden sollen und wie die Ausgabe aussehen
63
- # soll, wird dem Sequencer über seine Konfiguration mitgeteilt.
64
- #
65
- # === Mögliche Verlinkung
66
- # Erwartet:: Daten vom Typ *Word* z.B. von Wordsearcher, Decomposer, Ocr_variator, Multiworder
67
- # Erzeugt:: Daten vom Typ *Word* (mit Attribut WA_SEQUENCE). Je erkannter Mehrwortgruppe wird ein zusätzliches Word-Objekt in den Datenstrom eingefügt. Z.B. für Ocr_variator, Sequencer, Noneword_filter, Vector_filter
68
- #
69
- # === Parameter
70
- # Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung).
71
- # Alle anderen Parameter müssen zwingend angegeben werden.
72
- # <b>in</b>:: siehe allgemeine Beschreibung des Attendee
73
- # <b>out</b>:: siehe allgemeine Beschreibung des Attendee
74
- # <b><i>stopper</i></b>:: (Standard: TA_PUNCTUATION, TA_OTHER) Gibt die Begrenzungen an, zwischen
75
- # denen der Sequencer suchen soll, i.d.R. Satzzeichen und Sonderzeichen,
76
- # weil sie kaum in einer Mehrwortgruppen vorkommen.
77
- #
78
- # === Konfiguration
79
- # Der Sequencer benötigt zur Identifikation von Mehrwortgruppen Regeln, nach denen er
80
- # arbeiten soll. Die benötigten Regeln werden nicht als Parameter, sondern in der
81
- # Sprachkonfiguration hinterlegt, die sich standardmäßig in der Datei
82
- # <tt>de.lang</tt> befindet (YAML-Format).
83
- # language:
84
- # attendees:
85
- # sequencer:
86
- # sequences: [ [AS, "2, 1"], [AK, "2, 1"] ]
87
- # Hiermit werden dem Sequencer zwei Regeln mitgeteilt: Er soll Adjektiv-Substantiv- (AS) und
88
- # Adjektiv-Kompositum-Folgen (AK) erkennen. Zusätzlich ist angegeben, in welchem Format die
89
- # dadurch ermittelte Wortfolge ausgegeben werden soll. In diesem Beispiel also zuerst das
90
- # Substantiv und durch Komma getrennt das Adjektiv.
91
- #
92
- # === Beispiele
93
- # Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration <tt>t1.cfg</tt>
94
- # meeting:
95
- # attendees:
96
- # - textreader: { out: lines, files: '$(files)' }
97
- # - tokenizer: { in: lines, out: token }
98
- # - wordsearcher: { in: token, out: words, source: 'sys-dic' }
99
- # - sequencer: { in: words, out: seque }
100
- # - debugger: { in: seque, prompt: 'out>' }
101
- # ergibt die Ausgabe über den Debugger: <tt>lingo -c t1 test.txt</tt>
102
- # out> *FILE('test.txt')
103
- # out> <Lingo|?>
104
- # out> <kann = [(koennen/v)]>
105
- # out> <indexierung, automatisch|SEQ = [(indexierung, automatisch/q)]>
106
- # out> <automatische = [(automatisch/a)]>
107
- # out> <Indexierung = [(indexierung/s)]>
108
- # out> <und = [(und/w)]>
109
- # out> <indexierung, genial|SEQ = [(indexierung, genial/q)]>
110
- # out> <geniale = [(genial/a), (genialisch/a)]>
111
- # out> <Indexierung = [(indexierung/s)]>
112
- # out> :./PUNC:
113
- # out> *EOL('test.txt')
114
- # out> *EOF('test.txt')
115
-
116
- class Attendee::Sequencer < BufferedAttendee
117
-
118
- protected
119
-
120
- def init
121
- # Parameter verwerten
122
- @stopper = get_array('stopper', TA_PUNCTUATION + ',' + TA_OTHER).map { |s| s.upcase }
123
- @seq_strings = get_key('sequences').map { |e| WordSequence.new(*e) }
124
-
125
- forward(STR_CMD_ERR, 'Konfiguration ist leer') if @seq_strings.empty?
126
- end
121
+ unless @buffer.size < 2
122
+ matches = Hash.new { |h, k| h[k] = [] }
127
123
 
128
- def control(cmd, par)
129
- # Jedes Control-Object ist auch Auslöser der Verarbeitung
130
- process_buffer if [STR_CMD_RECORD, STR_CMD_EOF].include?(cmd)
131
- end
124
+ sequences(@buffer.map { |obj|
125
+ obj.is_a?(Word) && !obj.unknown? ? obj.attrs(false) : ['#']
126
+ }).uniq.each { |sequence|
127
+ @seq_strings.each { |wordseq|
128
+ wordseq.scan(sequence) { |pos, form, classes|
129
+ inc('Anzahl erkannter Sequenzen')
132
130
 
133
- def process_buffer?
134
- # start buffer processing when stopper token are found or at unknown words
135
- item = @buffer.last
136
- (item.is_a?(StringA) && @stopper.include?(item.attr.upcase)) ||
137
- (item.is_a?(Word) && item.unknown?)
138
- end
131
+ classes.each_with_index { |wc, index|
132
+ @buffer[pos + index].lexicals.find { |lex|
133
+ form.gsub!(index.succ.to_s, lex.form) if lex.attr == wc
134
+ } or break
135
+ } or next
136
+
137
+ matches[pos] << form
138
+ }
139
+ }
140
+ }
139
141
 
140
- def process_buffer
141
- return if @buffer.empty?
142
+ matches.sort.each { |pos, forms|
143
+ forms.uniq.each { |form|
144
+ deferred_insert(pos, Word.new_lexical(form, WA_SEQUENCE, LA_SEQUENCE))
145
+ }
146
+ }
147
+ end
142
148
 
143
- unless @buffer.size < 2
144
- matches = Hash.new { |h, k| h[k] = [] }
149
+ forward_buffer
150
+ end
145
151
 
146
- sequences(@buffer.map { |obj|
147
- obj.is_a?(Word) && !obj.unknown? ? obj.attrs(false) : ['#']
148
- }).uniq.each { |sequence|
149
- @seq_strings.each { |wordseq|
150
- wordseq.scan(sequence) { |pos, form, classes|
151
- inc('Anzahl erkannter Sequenzen')
152
+ private
152
153
 
153
- classes.each_with_index { |wc, index|
154
- @buffer[pos + index].lexicals.find { |lex|
155
- form.gsub!(index.succ.to_s, lex.form) if lex.attr == wc
156
- } or break
157
- } or next
154
+ def sequences(map)
155
+ res = map.shift
158
156
 
159
- matches[pos] << form
160
- }
161
- }
157
+ map.each { |classes|
158
+ temp = []
159
+ res.each { |wc1| classes.each { |wc2| temp << (wc1 + wc2) } }
160
+ res = temp
162
161
  }
163
162
 
164
- matches.sort.each { |pos, forms|
165
- forms.uniq.each { |form|
166
- deferred_insert(pos, Word.new_lexical(form, WA_SEQUENCE, LA_SEQUENCE))
167
- }
168
- }
163
+ res
169
164
  end
170
165
 
171
- forward_buffer
172
- end
166
+ class WordSequence
173
167
 
174
- private
168
+ attr_reader :classes, :format, :string
175
169
 
176
- def sequences(map)
177
- res = map.shift
170
+ def initialize(wordclasses, format)
171
+ @string = wordclasses.downcase
172
+ @classes = @string.split(//)
173
+ @format = format
174
+ end
178
175
 
179
- map.each { |classes|
180
- temp = []
181
- res.each { |wc1| classes.each { |wc2| temp << (wc1 + wc2) } }
182
- res = temp
183
- }
176
+ def scan(sequence)
177
+ pos = 0
178
+
179
+ while pos = sequence.index(string, pos)
180
+ yield pos, format.dup, classes
181
+ pos += 1
182
+ end
183
+ end
184
+
185
+ end
184
186
 
185
- res
186
187
  end
187
188
 
188
189
  end