lingo 1.8.2 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/ChangeLog +33 -0
  2. data/README +6 -5
  3. data/Rakefile +6 -4
  4. data/{lib/lingo/cachable.rb → bin/lingosrv} +30 -58
  5. data/bin/lingoweb +30 -0
  6. data/de.lang +2 -13
  7. data/en/lingo-irr.txt +266 -0
  8. data/en/lingo-wdn.txt +37319 -0
  9. data/en.lang +2 -15
  10. data/lib/lingo/app.rb +82 -0
  11. data/lib/lingo/attendee/abbreviator.rb +22 -26
  12. data/lib/lingo/attendee/debugger.rb +8 -4
  13. data/lib/lingo/attendee/decomposer.rb +0 -1
  14. data/lib/lingo/attendee/dehyphenizer.rb +2 -2
  15. data/lib/lingo/attendee/multi_worder.rb +20 -13
  16. data/lib/lingo/attendee/noneword_filter.rb +2 -7
  17. data/lib/lingo/attendee/sequencer.rb +43 -19
  18. data/lib/lingo/attendee/stemmer/porter.rb +2 -2
  19. data/lib/lingo/attendee/stemmer.rb +1 -1
  20. data/lib/lingo/attendee/synonymer.rb +1 -9
  21. data/lib/lingo/attendee/text_reader.rb +42 -29
  22. data/lib/lingo/attendee/text_writer.rb +3 -6
  23. data/lib/lingo/attendee/tokenizer.rb +87 -69
  24. data/lib/lingo/attendee/variator.rb +7 -5
  25. data/lib/lingo/attendee/vector_filter.rb +11 -11
  26. data/lib/lingo/attendee/word_searcher.rb +1 -9
  27. data/lib/lingo/attendee.rb +24 -105
  28. data/lib/lingo/buffered_attendee.rb +2 -9
  29. data/lib/lingo/call.rb +18 -13
  30. data/lib/lingo/cli.rb +5 -10
  31. data/lib/lingo/config.rb +40 -7
  32. data/lib/lingo/ctl.rb +69 -57
  33. data/lib/lingo/database/hash_store.rb +9 -4
  34. data/lib/lingo/database/sdbm_store.rb +4 -7
  35. data/lib/lingo/database/source/multi_key.rb +1 -1
  36. data/lib/lingo/database/source/multi_value.rb +1 -1
  37. data/lib/lingo/database/source.rb +2 -20
  38. data/lib/lingo/database.rb +30 -19
  39. data/lib/lingo/debug.rb +79 -0
  40. data/lib/lingo/{core_ext.rb → language/char.rb} +43 -42
  41. data/lib/lingo/language/dictionary.rb +38 -46
  42. data/lib/lingo/language/grammar.rb +40 -57
  43. data/lib/lingo/language/lexical.rb +4 -7
  44. data/lib/lingo/language/lexical_hash.rb +17 -35
  45. data/lib/lingo/language/token.rb +4 -0
  46. data/lib/lingo/language/word.rb +7 -8
  47. data/lib/lingo/language/word_form.rb +4 -4
  48. data/lib/lingo/language.rb +2 -1
  49. data/lib/lingo/srv/config.ru +4 -0
  50. data/lib/lingo/srv/lingosrv.cfg +14 -0
  51. data/lib/lingo/{reportable.rb → srv.rb} +59 -61
  52. data/lib/lingo/version.rb +1 -1
  53. data/lib/lingo/web/config.ru +4 -0
  54. data/lib/lingo/web/lingoweb.cfg +14 -0
  55. data/lib/lingo/web/public/lingo.png +0 -0
  56. data/lib/lingo/web/public/lingoweb.css +74 -0
  57. data/lib/lingo/web/views/index.erb +92 -0
  58. data/lib/lingo/web.rb +94 -0
  59. data/lib/lingo.rb +27 -29
  60. data/lingo.cfg +1 -1
  61. data/lir.cfg +24 -0
  62. data/ru/lingo-dic.txt +22342 -0
  63. data/ru/lingo-mul.txt +5151 -0
  64. data/ru/lingo-syn.txt +0 -0
  65. data/ru.lang +99 -0
  66. data/test/attendee/ts_sequencer.rb +2 -2
  67. data/test/attendee/ts_text_reader.rb +36 -2
  68. data/test/attendee/ts_text_writer.rb +6 -6
  69. data/test/lir.vec +3 -3
  70. data/test/test_helper.rb +104 -102
  71. data/test/ts_database.rb +1 -1
  72. data/test/ts_language.rb +55 -96
  73. data/txt/artikel-ru.txt +45 -0
  74. data/txt/lir.txt +1 -3
  75. metadata +143 -83
  76. data/TODO +0 -23
@@ -1,42 +1,43 @@
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
- require 'unicode'
28
-
29
- class String
30
-
31
- alias_method :_lingo_original_downcase, :downcase
32
- alias_method :_lingo_original_downcase!, :downcase!
33
-
34
- def downcase
35
- Unicode.downcase(self)
36
- end
37
-
38
- def downcase!
39
- replace(downcase)
40
- end
41
-
42
- end
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
+ module Language
30
+
31
+ module Char
32
+
33
+ ANY = [
34
+ CHAR = '[[:alpha:]]',
35
+ DIGIT = '[[:digit:]]',
36
+ LEGAL = '[ /&()\[\].,\'<>-]'
37
+ ].join('|')
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -30,8 +30,7 @@ class Lingo
30
30
 
31
31
  class Dictionary
32
32
 
33
- include Cachable
34
- include Reportable
33
+ KEY_REF_RE = %r{\A#{Database::KEY_REF_ESC}\d+}
35
34
 
36
35
  def self.open(*args)
37
36
  yield dictionary = new(*args)
@@ -41,12 +40,9 @@ class Lingo
41
40
 
42
41
  def initialize(config, lingo)
43
42
  unless config.has_key?('source')
44
- raise ArgumentError, 'Required parameter `source\' missing.'
43
+ raise ArgumentError, "Required parameter `source' missing."
45
44
  end
46
45
 
47
- init_cachable
48
- init_reportable
49
-
50
46
  @suffixes, @infixes = [], []
51
47
 
52
48
  Array(lingo.dictionary_config['suffix']).each { |t, s|
@@ -67,56 +63,49 @@ class Lingo
67
63
  end
68
64
 
69
65
  def close
70
- @src.each(&:close)
71
- end
72
-
73
- def report
74
- super.tap { |rep| @src.each { |src| rep.update(src.report) } }
66
+ @src.each { |i| i.close }
75
67
  end
76
68
 
77
69
  # _dic_.find_word( _aString_ ) -> _aNewWord_
78
70
  #
79
71
  # Erstellt aus dem String ein Wort und sucht nach diesem im Wörterbuch.
80
72
  def find_word(str)
81
- if hit?(key = str.downcase)
82
- inc('cache hits')
83
- return retrieve(key).tap { |word| word.form = str }
84
- end
85
-
86
- word = Word.new(str, WA_UNKNOWN)
87
-
88
- unless (lexicals = select_with_suffix(str)).empty?
89
- word.lexicals = lexicals
90
- word.attr = WA_IDENTIFIED
91
- end
92
-
93
- store(key, word)
73
+ (@_word ||= {})[str] ||= Word.new(str, WA_UNKNOWN).tap { |w|
74
+ unless (lexicals = select_with_suffix(str)).empty?
75
+ w.lexicals = lexicals
76
+ w.attr = WA_IDENTIFIED
77
+ end
78
+ }
94
79
  end
95
80
 
96
- def find_synonyms(obj)
81
+ def find_synonyms(obj, syn = [])
97
82
  lex = obj.lexicals
98
83
  lex = [obj] if lex.empty? && obj.unknown?
99
84
 
100
- # multiworder optimization
101
- ref = %r{\A#{Database::KEY_REF_ESC}\d+}
85
+ com, ref = obj.attr == WA_COMPOUND, KEY_REF_RE
102
86
 
103
- lex.each_with_object([]) { |l, s|
104
- next if l.attr == LA_SYNONYM
105
- next if l.attr != LA_COMPOUND && obj.attr == WA_COMPOUND
106
-
107
- select(l.form).each { |y| s << y unless y =~ ref }
87
+ lex.each { |l|
88
+ select(l.form, syn) { |i| i =~ ref } unless com &&
89
+ l.attr != LA_COMPOUND || l.attr == LA_SYNONYM
108
90
  }
91
+
92
+ syn
109
93
  end
110
94
 
111
95
  # _dic_.select( _aString_ ) -> _ArrayOfLexicals_
112
96
  #
113
97
  # Sucht alle Wörterbücher durch und gibt den ersten Treffer zurück (+mode = first+), oder alle Treffer (+mode = all+)
114
- def select(str)
115
- @src.each_with_object([]) { |src, lex|
98
+ def select(str, lex = [])
99
+ @src.each { |src|
116
100
  l = src[str] or next
117
- lex.concat(l)
118
- break lex unless @all
119
- }.tap { |lex| lex.sort!; lex.uniq! }
101
+ lex.concat(block_given? ? l.delete_if { |i| yield i } : l)
102
+ break unless @all
103
+ }
104
+
105
+ lex.sort!
106
+ lex.uniq!
107
+
108
+ lex
120
109
  end
121
110
 
122
111
  # _dic_.select_with_suffix( _aString_ ) -> _ArrayOfLexicals_
@@ -154,19 +143,22 @@ class Lingo
154
143
  private
155
144
 
156
145
  def select_with_affix(affix, str)
157
- select(str).tap { |l|
158
- if l.empty?
159
- affix_lexicals(affix, str).each { |a| select(a.form).each { |b|
160
- l << b if affix != :suffix || a.attr == b.attr
161
- } }
162
- end
163
- }
146
+ lex = select(str)
147
+
148
+ affix_lexicals(affix, str).each { |a| select(a.form, lex) { |b|
149
+ affix == :suffix && a.attr != b.attr
150
+ } } if lex.empty?
151
+
152
+ lex
164
153
  end
165
154
 
166
155
  def affix_lexicals(affix, str)
167
- instance_variable_get("@#{affix}es").each_with_object([]) { |(r, e, t), l|
168
- l << Lexical.new("#{$`}#{e == '*' ? '' : e}#{$'}", t) if str =~ r
156
+ lex = instance_variable_get("@#{affix}es").map { |r, e, t|
157
+ Lexical.new("#{$`}#{e == '*' ? '' : e}#{$'}", t) if str =~ r
169
158
  }
159
+
160
+ lex.compact!
161
+ lex
170
162
  end
171
163
 
172
164
  end
@@ -35,9 +35,6 @@ class Lingo
35
35
 
36
36
  class Grammar
37
37
 
38
- include Cachable
39
- include Reportable
40
-
41
38
  HYPHEN_RE = %r{\A(.+)-([^-]+)\z}
42
39
 
43
40
  def self.open(*args)
@@ -47,11 +44,10 @@ class Lingo
47
44
  end
48
45
 
49
46
  def initialize(config, lingo)
50
- init_cachable
51
- init_reportable
52
-
53
47
  @dic, @suggestions = Dictionary.new(config, lingo), []
54
48
 
49
+ lingo.deprecate(:compositum, :compound, self) if lingo.dictionary_config.has_key?('compositum')
50
+
55
51
  cfg = lingo.dictionary_config['compound'] ||
56
52
  lingo.dictionary_config['compositum'] # DEPRECATE compositum
57
53
 
@@ -70,80 +66,63 @@ class Lingo
70
66
  # Bestimmte Sequenzen können als ungültige Komposita erkannt werden,
71
67
  # z.B. ist ein Kompositum aus zwei Adjetiven kein Kompositum, also
72
68
  # skip-sequence = 'aa'
73
- @sequences = cfg.fetch('skip-sequences', []).map!(&:downcase)
69
+ @sequences = cfg.fetch('skip-sequences', []).map! { |i| i.downcase }
74
70
  end
75
71
 
76
72
  def close
77
73
  @dic.close
78
74
  end
79
75
 
80
- def report
81
- super.update(@dic.report)
82
- end
83
-
84
76
  # find_compound(str) -> word wenn level=1
85
77
  # find_compound(str) -> [lex, sta] wenn level!=1
86
78
  #
87
79
  # find_compound arbeitet in verschiedenen Leveln, da die Methode auch rekursiv aufgerufen wird. Ein Level größer 1
88
80
  # entspricht daher einem rekursiven Aufruf
89
81
  def find_compound(str, level = 1, tail = false)
90
- key, top, empty = str.downcase, level == 1, [[], [], '']
91
-
92
- if top && hit?(key)
93
- inc('cache hits')
94
- return retrieve(key)
95
- end
96
-
97
- com = Word.new(str, WA_UNKNOWN)
82
+ return permute_compound([[], [], ''], str, level, tail) if level != 1
83
+
84
+ (@_compound ||= {})[str] ||= permute_compound(
85
+ com = Word.new(str, WA_UNKNOWN), str, level, tail
86
+ ) { |lex|
87
+ com.attr = WA_COMPOUND
88
+ com.lexicals = lex.each { |l|
89
+ l.attr += @append_wc unless l.attr == LA_COMPOUND
90
+ }
91
+ }
92
+ end
98
93
 
99
- unless str.length > @min_word_size
100
- inc('String zu kurz')
101
- return top ? com : empty
102
- end
94
+ private
103
95
 
104
- inc('Komposita geprüft')
96
+ def permute_compound(ret, str, level, tail)
97
+ if (len = str.length) > @min_word_size
98
+ str = Unicode.downcase(str)
105
99
 
106
- lex, sta, seq = res = permute_compound(key, level, tail)
100
+ lex, sta, seq = res = if str =~ HYPHEN_RE
101
+ test_compound($1, '-', $2, level, tail)
102
+ else
103
+ sug = @suggestions[level] ||= []
107
104
 
108
- val = !lex.empty? &&
109
- sta.size <= @max_parts &&
110
- sta.min >= @min_part_size &&
111
- str.length / sta.size >= @min_avg_part_size &&
112
- (@sequences.empty? || !@sequences.include?(seq))
105
+ catch(:res) {
106
+ 1.upto(len - 1) { |i|
107
+ tst = test_compound(str[0, i], '', str[i, len], level, tail)
113
108
 
114
- if top
115
- if val
116
- inc('Komposita erkannt')
109
+ unless (lex = tst.first).empty?
110
+ lex.last.attr == LA_TAKEITASIS ? sug << tst : throw(:res, tst)
111
+ end
112
+ }
117
113
 
118
- com.attr = WA_COMPOUND
119
- com.lexicals = lex.map { |l|
120
- l.attr == LA_COMPOUND ? l :
121
- Lexical.new(l.form, l.attr + @append_wc)
114
+ sug.empty? ? [[], [], ''] : sug.first.tap { sug.clear }
122
115
  }
123
116
  end
124
117
 
125
- store(key, com)
126
- else
127
- val ? res : empty
118
+ block_given? ? yield(lex) : ret = res if !lex.empty? &&
119
+ sta.size <= @max_parts &&
120
+ sta.min >= @min_part_size &&
121
+ str.length / sta.size >= @min_avg_part_size &&
122
+ (@sequences.empty? || !@sequences.include?(seq))
128
123
  end
129
- end
130
-
131
- # permute_compound( _aString_ ) -> [lex, sta, seq]
132
- def permute_compound(str, level = 1, tail = false)
133
- return test_compound($1, '-', $2, level, tail) if str =~ HYPHEN_RE
134
-
135
- sug, len = @suggestions[level] ||= [], str.length
136
-
137
- 1.upto(len - 1) { |i|
138
- res = test_compound(str[0, i], '', str[i, len], level, tail)
139
124
 
140
- unless (lex = res.first).empty?
141
- return res unless lex.last.attr == LA_TAKEITASIS
142
- sug << res
143
- end
144
- }
145
-
146
- sug.empty? ? [[], [], ''] : sug.first.tap { sug.clear }
125
+ ret
147
126
  end
148
127
 
149
128
  # test_compound() -> [lex, sta, seq]
@@ -189,6 +168,10 @@ class Lingo
189
168
  end
190
169
  end
191
170
 
171
+ { flex => fform, blex => bform }.each { |a, f|
172
+ a.each { |l| l.src ||= f }
173
+ }
174
+
192
175
  flex.concat(blex).delete_if { |l| l.attr == LA_COMPOUND }.
193
176
  push(Lexical.new(fform + infix + bform, LA_COMPOUND)).sort!
194
177
 
@@ -40,14 +40,11 @@ class Lingo
40
40
  return 1 unless other.is_a?(self.class)
41
41
 
42
42
  a1, a2 = attr, other.attr
43
+ return form <=> other.form if a1 == a2
43
44
 
44
- if a1 == a2
45
- form <=> other.form
46
- else
47
- a1.empty? ? 1 : a2.empty? ? -1 : begin
48
- i1, i2 = [a1, a2].map(&LA_SORTORDER.method(:index))
49
- i1 ? i2 ? i2 <=> i1 : -1 : i2 ? 1 : a1 <=> a2
50
- end
45
+ a1.empty? ? 1 : a2.empty? ? -1 : begin
46
+ i1, i2 = LA_SORTORDER.values_at(a1, a2)
47
+ i1 ? i2 ? i1 <=> i2 : -1 : i2 ? 1 : a1 <=> a2
51
48
  end
52
49
  end
53
50
 
@@ -34,9 +34,6 @@ class Lingo
34
34
 
35
35
  class LexicalHash
36
36
 
37
- include Cachable
38
- include Reportable
39
-
40
37
  def self.open(*args)
41
38
  yield lexical_hash = new(*args)
42
39
  ensure
@@ -44,9 +41,6 @@ class Lingo
44
41
  end
45
42
 
46
43
  def initialize(id, lingo)
47
- init_cachable
48
- init_reportable(id)
49
-
50
44
  @wc = lingo.database_config(id).fetch('def-wc', LA_UNKNOWN)
51
45
  @src = Database.open(id, lingo)
52
46
  end
@@ -56,35 +50,23 @@ class Lingo
56
50
  end
57
51
 
58
52
  def [](key)
59
- inc('total requests')
60
- key = key.downcase
61
-
62
- if hit?(key)
63
- inc('cache hits')
64
- return retrieve(key)
65
- end
66
-
67
- inc('source reads')
68
-
69
- if record = @src[key]
70
- record = record.map { |str|
71
- case str
72
- when /^\*\d+$/ then str
73
- when /^#(.)$/ then Lexical.new(key, $1)
74
- when /^([^#]+?)\s*#(.)$/ then Lexical.new($1, $2)
75
- when /^([^#]+)$/ then Lexical.new($1, @wc)
76
- else str
77
- end
78
- }
79
-
80
- record.compact!
81
- record.sort!
82
- record.uniq!
83
-
84
- inc('data found')
85
- end
86
-
87
- store(key, record)
53
+ rec = @src[key = Unicode.downcase(key)] or return
54
+
55
+ res = rec.map { |str|
56
+ case str
57
+ when /^\*\d+$/ then str
58
+ when /^#(.)$/ then Lexical.new(key, $1)
59
+ when /^([^#]+?)\s*#(.)$/ then Lexical.new($1, $2)
60
+ when /^([^#]+)$/ then Lexical.new($1, @wc)
61
+ else str
62
+ end
63
+ }
64
+
65
+ res.compact!
66
+ res.sort!
67
+ res.uniq!
68
+
69
+ res
88
70
  end
89
71
 
90
72
  end
@@ -37,6 +37,10 @@ class Lingo
37
37
 
38
38
  class Token < WordForm
39
39
 
40
+ def word?
41
+ attr == TA_WORD
42
+ end
43
+
40
44
  def to_s
41
45
  ":#{super}:"
42
46
  end
@@ -80,16 +80,14 @@ class Lingo
80
80
  end
81
81
 
82
82
  def add_lexicals(lex)
83
- @lexicals.concat(lex)
84
-
85
- @lexicals.sort!
86
- @lexicals.uniq!
87
-
88
- self
83
+ unless lex.empty?
84
+ @lexicals.concat(lex).uniq!
85
+ @lexicals.sort!
86
+ end
89
87
  end
90
88
 
91
89
  def attrs(compound_parts = true)
92
- lexicals(compound_parts).map(&:attr)
90
+ lexicals(compound_parts).map { |i| i.attr }
93
91
  end
94
92
 
95
93
  def parts
@@ -125,7 +123,8 @@ class Lingo
125
123
  end
126
124
 
127
125
  def <<(*other)
128
- lexicals.concat(other.tap(&:flatten!))
126
+ other.flatten!
127
+ lexicals.concat(other)
129
128
  self
130
129
  end
131
130
 
@@ -36,10 +36,10 @@ class Lingo
36
36
 
37
37
  include Comparable
38
38
 
39
- attr_accessor :form, :attr
39
+ attr_accessor :form, :attr, :src
40
40
 
41
- def initialize(form, attr = '-')
42
- @form, @attr = form || '', attr || ''
41
+ def initialize(form, attr = '-', src = nil)
42
+ @form, @attr, @src = form || '', attr || '', src
43
43
  end
44
44
 
45
45
  def unknown?
@@ -67,7 +67,7 @@ class Lingo
67
67
  end
68
68
 
69
69
  def hash
70
- to_s.hash
70
+ to_a.hash
71
71
  end
72
72
 
73
73
  def eql?(other)
@@ -31,6 +31,7 @@ require_relative 'language/word_form'
31
31
  require_relative 'language/token'
32
32
  require_relative 'language/lexical'
33
33
  require_relative 'language/word'
34
+ require_relative 'language/char'
34
35
 
35
36
  class Lingo
36
37
 
@@ -72,7 +73,7 @@ class Lingo
72
73
  LA_SYNONYM = 'y',
73
74
  LA_STEM = 'z',
74
75
  LA_UNKNOWN = '?'
75
- ].reverse.join
76
+ ].each_with_index.inject({}) { |h, (i, j)| h[i] = j; h }
76
77
 
77
78
  end
78
79
 
@@ -0,0 +1,4 @@
1
+ require 'lingo'
2
+ require 'lingo/srv'
3
+
4
+ run Lingo::Srv
@@ -0,0 +1,14 @@
1
+ ---
2
+ meeting:
3
+ attendees:
4
+ - text_reader: { files: STDIN }
5
+
6
+ - tokenizer: { }
7
+ - word_searcher: { source: sys-dic, mode: first }
8
+ - decomposer: { source: sys-dic }
9
+ - multi_worder: { source: sys-mul }
10
+ - sequencer: { stopper: PUNC,OTHR }
11
+ - synonymer: { skip: '?,t', source: sys-syn }
12
+
13
+ - vector_filter: { debug: 'true', prompt: '' }
14
+ - text_writer: { ext: STDOUT, sep: "\n" }