lingo 1.8.2 → 1.8.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +33 -0
- data/README +6 -5
- data/Rakefile +6 -4
- data/{lib/lingo/cachable.rb → bin/lingosrv} +30 -58
- data/bin/lingoweb +30 -0
- data/de.lang +2 -13
- data/en/lingo-irr.txt +266 -0
- data/en/lingo-wdn.txt +37319 -0
- data/en.lang +2 -15
- data/lib/lingo/app.rb +82 -0
- data/lib/lingo/attendee/abbreviator.rb +22 -26
- data/lib/lingo/attendee/debugger.rb +8 -4
- data/lib/lingo/attendee/decomposer.rb +0 -1
- data/lib/lingo/attendee/dehyphenizer.rb +2 -2
- data/lib/lingo/attendee/multi_worder.rb +20 -13
- data/lib/lingo/attendee/noneword_filter.rb +2 -7
- data/lib/lingo/attendee/sequencer.rb +43 -19
- data/lib/lingo/attendee/stemmer/porter.rb +2 -2
- data/lib/lingo/attendee/stemmer.rb +1 -1
- data/lib/lingo/attendee/synonymer.rb +1 -9
- data/lib/lingo/attendee/text_reader.rb +42 -29
- data/lib/lingo/attendee/text_writer.rb +3 -6
- data/lib/lingo/attendee/tokenizer.rb +87 -69
- data/lib/lingo/attendee/variator.rb +7 -5
- data/lib/lingo/attendee/vector_filter.rb +11 -11
- data/lib/lingo/attendee/word_searcher.rb +1 -9
- data/lib/lingo/attendee.rb +24 -105
- data/lib/lingo/buffered_attendee.rb +2 -9
- data/lib/lingo/call.rb +18 -13
- data/lib/lingo/cli.rb +5 -10
- data/lib/lingo/config.rb +40 -7
- data/lib/lingo/ctl.rb +69 -57
- data/lib/lingo/database/hash_store.rb +9 -4
- data/lib/lingo/database/sdbm_store.rb +4 -7
- data/lib/lingo/database/source/multi_key.rb +1 -1
- data/lib/lingo/database/source/multi_value.rb +1 -1
- data/lib/lingo/database/source.rb +2 -20
- data/lib/lingo/database.rb +30 -19
- data/lib/lingo/debug.rb +79 -0
- data/lib/lingo/{core_ext.rb → language/char.rb} +43 -42
- data/lib/lingo/language/dictionary.rb +38 -46
- data/lib/lingo/language/grammar.rb +40 -57
- data/lib/lingo/language/lexical.rb +4 -7
- data/lib/lingo/language/lexical_hash.rb +17 -35
- data/lib/lingo/language/token.rb +4 -0
- data/lib/lingo/language/word.rb +7 -8
- data/lib/lingo/language/word_form.rb +4 -4
- data/lib/lingo/language.rb +2 -1
- data/lib/lingo/srv/config.ru +4 -0
- data/lib/lingo/srv/lingosrv.cfg +14 -0
- data/lib/lingo/{reportable.rb → srv.rb} +59 -61
- data/lib/lingo/version.rb +1 -1
- data/lib/lingo/web/config.ru +4 -0
- data/lib/lingo/web/lingoweb.cfg +14 -0
- data/lib/lingo/web/public/lingo.png +0 -0
- data/lib/lingo/web/public/lingoweb.css +74 -0
- data/lib/lingo/web/views/index.erb +92 -0
- data/lib/lingo/web.rb +94 -0
- data/lib/lingo.rb +27 -29
- data/lingo.cfg +1 -1
- data/lir.cfg +24 -0
- data/ru/lingo-dic.txt +22342 -0
- data/ru/lingo-mul.txt +5151 -0
- data/ru/lingo-syn.txt +0 -0
- data/ru.lang +99 -0
- data/test/attendee/ts_sequencer.rb +2 -2
- data/test/attendee/ts_text_reader.rb +36 -2
- data/test/attendee/ts_text_writer.rb +6 -6
- data/test/lir.vec +3 -3
- data/test/test_helper.rb +104 -102
- data/test/ts_database.rb +1 -1
- data/test/ts_language.rb +55 -96
- data/txt/artikel-ru.txt +45 -0
- data/txt/lir.txt +1 -3
- metadata +143 -83
- data/TODO +0 -23
@@ -44,28 +44,10 @@ class Lingo
|
|
44
44
|
# === Konfiguration
|
45
45
|
# Der Tokenizer benötigt zur Identifikation einzelner Token Regeln, nach denen er
|
46
46
|
# arbeiten soll. Die benötigten Regeln werden aufgrund des Umfangs nicht als Parameter,
|
47
|
-
# sondern in
|
48
|
-
# <tt>de.lang</tt> befindet (YAML-Format).
|
49
|
-
# language:
|
50
|
-
# attendees:
|
51
|
-
# tokenizer:
|
52
|
-
# regulars:
|
53
|
-
# - _CHR_: '\wÄÖÜÁÂÀÉÊÈÍÎÌÓÔÒÚÛÙÝäöüáâàéêèíîìóôòúûùý'
|
54
|
-
# - NUMS: '[+-]?(\d{4,}|\d{1,3}(\.\d{3,3})*)(\.|(,\d+)?%?)'
|
55
|
-
# - URLS: '((mailto:|(news|http|https|ftp|ftps)://)\S+|^(www(\.\S+)+)|\S+([\._]\S+)+@\S+(\.\S+)+)'
|
56
|
-
# - ABRV: '(([_CHR_]+\.)+)[_CHR_]+'
|
57
|
-
# - ABRS: '(([_CHR_]{1,1}\.)+)(?!\.\.)'
|
58
|
-
# - WORD: '[_CHR_\d]+'
|
59
|
-
# - PUNC: '[!,\.:;?]'
|
60
|
-
# - OTHR: '[!\"#$%&()*\+,\-\./:;<=>?@\[\\\]^_`{|}~´]'
|
61
|
-
# - HELP: '.*'
|
47
|
+
# sondern in einer Programmkonstanten hinterlegt.
|
62
48
|
# Die Regeln werden in der angegebenen Reihenfolge abgearbeitet, solange bis ein Token
|
63
49
|
# erkannt wurde. Sollte keine Regel zutreffen, so greift die letzt Regel +HELP+ in jedem
|
64
50
|
# Fall.
|
65
|
-
# Regeln, deren Name in Unterstriche eingefasst sind, werden als Makro interpretiert.
|
66
|
-
# Makros werden genutzt, um lange oder sich wiederholende Bestandteile von Regeln
|
67
|
-
# einmalig zu definieren und in den Regeln über den Makronamen eine Auflösung zu forcieren.
|
68
|
-
# Makros werden selber nicht für die Erkennung von Token eingesetzt.
|
69
51
|
#
|
70
52
|
# === Generierte Kommandos
|
71
53
|
# Damit der nachfolgende Datenstrom einwandfrei verarbeitet werden kann, generiert der Tokenizer
|
@@ -98,33 +80,79 @@ class Lingo
|
|
98
80
|
|
99
81
|
class Tokenizer < self
|
100
82
|
|
83
|
+
CHAR, DIGIT = Char::CHAR, Char::DIGIT
|
84
|
+
|
85
|
+
RULES = [
|
86
|
+
['WIKI', /^=+.+=+$/],
|
87
|
+
['SPAC', /^\s+/],
|
88
|
+
['HTML', /^<[^>]+>/],
|
89
|
+
['WIKI', /^\[\[.+?\]\]/],
|
90
|
+
['NUMS', /^[+-]?(?:\d{4,}|\d{1,3}(?:\.\d{3,3})*)(?:\.|(?:,\d+)?%?)/],
|
91
|
+
['URLS', /^(?:(?:mailto:|(?:news|https?|ftps?):\/\/)\S+|^(?:www(?:\.\S+)+)|[^\s.]+(?:[\._]\S+)+@\S+(?:\.\S+)+)/],
|
92
|
+
['ABRV', /^(?:(?:(?:#{CHAR})+\.)+)(?:#{CHAR})+/],
|
93
|
+
['WORD', /^(?:#{CHAR}|#{DIGIT}|-)+/],
|
94
|
+
['PUNC', /^[!,.:;?¡¿]/],
|
95
|
+
['OTHR', /^["$#%&'()*+\-\/<=>@\[\\\]^_{|}~¢£¤¥¦§¨©«¬®¯°±²³´¶·¸¹»¼½¾×÷]/],
|
96
|
+
['HELP', /^[^ ]*/]
|
97
|
+
]
|
98
|
+
|
99
|
+
class << self
|
100
|
+
|
101
|
+
def rule(name)
|
102
|
+
RULES.assoc(name)
|
103
|
+
end
|
104
|
+
|
105
|
+
def delete(*names)
|
106
|
+
names.each { |name| RULES.delete(rule(name)) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def replace(name, expr)
|
110
|
+
rule = rule(name) or return
|
111
|
+
rule[1] = block_given? ? yield(rule[1]) : expr
|
112
|
+
end
|
113
|
+
|
114
|
+
def insert(*rules)
|
115
|
+
_insert(0, rules)
|
116
|
+
end
|
117
|
+
|
118
|
+
def append(*rules)
|
119
|
+
_insert(-1, rules)
|
120
|
+
end
|
121
|
+
|
122
|
+
def insert_before(name, *rules)
|
123
|
+
_insert_name(name, rules, 0)
|
124
|
+
end
|
125
|
+
|
126
|
+
def insert_after(name, *rules)
|
127
|
+
_insert_name(name, rules, -1)
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def _insert(index, rules)
|
133
|
+
rules.push(*rules.pop) if rules.last.is_a?(Hash)
|
134
|
+
RULES.insert(index, *rules)
|
135
|
+
end
|
136
|
+
|
137
|
+
def _insert_name(name, rules, offset)
|
138
|
+
index = RULES.index(rule(name))
|
139
|
+
_insert(index ? index - offset : offset, rules)
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
101
144
|
protected
|
102
145
|
|
103
146
|
def init
|
104
147
|
@space = get_key('space', false)
|
105
|
-
@tags = get_key('tags',
|
106
|
-
@wiki = get_key('wiki',
|
107
|
-
|
108
|
-
# default rules
|
109
|
-
@rules = [['SPAC', /^\s+/]]
|
110
|
-
@rules << ['HTML', /^<[^>]+>/] unless @tags
|
111
|
-
@rules << ['WIKI', /^\[\[.+?\]\]/] unless @wiki
|
112
|
-
@rules.unshift(['WIKI', /^=+.+=+$/]) unless @wiki
|
113
|
-
|
114
|
-
get_key('regulars', []).each_with_object({}) { |rule, macros|
|
115
|
-
expr = rule.values.first.gsub(/_(\w+?)_/) {
|
116
|
-
macros[$&] || begin
|
117
|
-
Database::Source.const_get("UTF8_#{$1.upcase}")
|
118
|
-
rescue NameError
|
119
|
-
end
|
120
|
-
}
|
148
|
+
@tags = get_key('tags', false)
|
149
|
+
@wiki = get_key('wiki', false)
|
121
150
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
}
|
151
|
+
skip = []
|
152
|
+
skip << 'HTML' unless @tags
|
153
|
+
skip << 'WIKI' unless @wiki
|
154
|
+
|
155
|
+
@rules = RULES.reject { |name, _| skip.include?(name) }
|
128
156
|
|
129
157
|
@filename = @cont = nil
|
130
158
|
end
|
@@ -139,15 +167,7 @@ class Lingo
|
|
139
167
|
|
140
168
|
def process(obj)
|
141
169
|
if obj.is_a?(String)
|
142
|
-
|
143
|
-
|
144
|
-
tokenize(obj) { |form, attr|
|
145
|
-
inc("Anzahl Muster #{attr}")
|
146
|
-
inc('Anzahl Token')
|
147
|
-
|
148
|
-
forward(Token.new(form, attr))
|
149
|
-
}
|
150
|
-
|
170
|
+
tokenize(obj) { |*i| forward(Token.new(*i)) }
|
151
171
|
forward(STR_CMD_EOL, @filename) if @filename
|
152
172
|
else
|
153
173
|
forward(obj)
|
@@ -157,44 +177,42 @@ class Lingo
|
|
157
177
|
private
|
158
178
|
|
159
179
|
# tokenize("Eine Zeile.") -> [:Eine/WORD:, :Zeile/WORD:, :./PUNC:]
|
160
|
-
def tokenize(
|
180
|
+
def tokenize(line)
|
161
181
|
case @cont
|
162
182
|
when 'HTML'
|
163
|
-
if
|
183
|
+
if line =~ /^[^<>]*>/
|
164
184
|
yield $&, @cont
|
165
|
-
|
185
|
+
line, @cont = $', nil
|
166
186
|
else
|
167
|
-
yield
|
187
|
+
yield line, @cont
|
168
188
|
return
|
169
189
|
end
|
170
190
|
when 'WIKI'
|
171
|
-
if
|
191
|
+
if line =~ /^[^\[\]]*\]\]/
|
172
192
|
yield $&, @cont
|
173
|
-
|
193
|
+
line, @cont = $', nil
|
174
194
|
else
|
175
|
-
yield
|
195
|
+
yield line, @cont
|
176
196
|
return
|
177
197
|
end
|
178
198
|
when nil
|
179
|
-
if
|
199
|
+
if @tags && line =~ /<[^<>]*$/
|
180
200
|
yield $&, @cont = 'HTML'
|
181
|
-
|
201
|
+
line = $`
|
182
202
|
end
|
183
203
|
|
184
|
-
if
|
204
|
+
if @wiki && line =~ /\[\[[^\[\]]*$/
|
185
205
|
yield $&, @cont = 'WIKI'
|
186
|
-
|
206
|
+
line = $`
|
187
207
|
end
|
188
208
|
end
|
189
209
|
|
190
|
-
|
191
|
-
|
192
|
-
if
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
end
|
197
|
-
}
|
210
|
+
while (l = line.length) > 0 && @rules.find { |name, expr|
|
211
|
+
if line =~ expr
|
212
|
+
yield $&, name if name != 'SPAC' || @space
|
213
|
+
l == $'.length ? break : line = $'
|
214
|
+
end
|
215
|
+
}
|
198
216
|
end
|
199
217
|
end
|
200
218
|
|
@@ -90,22 +90,24 @@ class Lingo
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def control(cmd, param)
|
93
|
-
report_on(cmd, @dic, @gra)
|
94
93
|
end
|
95
94
|
|
96
95
|
def process(obj)
|
97
96
|
if obj.is_a?(Word) && @check[obj.attr]
|
98
|
-
|
97
|
+
vars, max = [obj.form], @max
|
99
98
|
|
100
|
-
@var.
|
101
|
-
|
99
|
+
@var.each { |args|
|
100
|
+
variate(vars, *args)
|
101
|
+
break unless vars.length < max
|
102
|
+
}
|
103
|
+
|
104
|
+
vars.each { |var|
|
102
105
|
next if (word = find_word(var)).unknown? || (
|
103
106
|
word.attr == WA_COMPOUND && word.lexicals.any? { |lex|
|
104
107
|
lex.attr.start_with?(LA_TAKEITASIS)
|
105
108
|
}
|
106
109
|
)
|
107
110
|
|
108
|
-
inc('Anzahl gefundener Wörter')
|
109
111
|
return forward(word.tap { word.form = @marker + var })
|
110
112
|
}
|
111
113
|
end
|
@@ -79,15 +79,20 @@ class Lingo
|
|
79
79
|
|
80
80
|
class VectorFilter < self
|
81
81
|
|
82
|
+
DEFAULT_SRC_SEP = '|'
|
83
|
+
|
82
84
|
protected
|
83
85
|
|
84
86
|
def init
|
85
87
|
if @debug = get_key('debug', false)
|
86
88
|
@prompt = get_key('prompt', 'lex:) ')
|
87
89
|
else
|
88
|
-
@lex =
|
90
|
+
@lex = get_re('lexicals', '[sy]')
|
89
91
|
@skip = get_array('skip', DEFAULT_SKIP, :upcase)
|
90
92
|
|
93
|
+
@src = get_key('src', false)
|
94
|
+
@src = DEFAULT_SRC_SEP if @src == true
|
95
|
+
|
91
96
|
if sort = get_key('sort', 'normal')
|
92
97
|
@sort_format, @sort_method = sort.downcase.split('_', 2)
|
93
98
|
end
|
@@ -111,25 +116,20 @@ class Lingo
|
|
111
116
|
elsif obj.is_a?(Word) && !@skip.include?(obj.attr)
|
112
117
|
@word_count += 1
|
113
118
|
|
114
|
-
|
115
|
-
vec = lex.form
|
119
|
+
obj.get_class(@lex).each { |lex|
|
120
|
+
vec = Unicode.downcase(lex.form)
|
121
|
+
vec << @src << lex.src if @src && lex.src
|
116
122
|
@sort_format ? @vectors << vec : forward(vec)
|
117
|
-
}
|
118
|
-
|
119
|
-
add('Anzahl von Vektor-Wörtern', cnt)
|
123
|
+
}
|
120
124
|
end
|
121
125
|
end
|
122
126
|
|
123
127
|
private
|
124
128
|
|
125
129
|
def send_vectors
|
126
|
-
add('Objekte gefiltert', @vectors.size)
|
127
|
-
|
128
130
|
if @sort_format == 'normal'
|
129
|
-
@vectors.sort!
|
130
131
|
@vectors.uniq!
|
131
|
-
|
132
|
-
@vectors.each(&method(:forward)).clear
|
132
|
+
flush(@vectors.sort!)
|
133
133
|
else
|
134
134
|
cnt, fmt = Hash.new(0), '%d'
|
135
135
|
|
@@ -72,18 +72,10 @@ class Lingo
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def control(cmd, param)
|
75
|
-
report_on(cmd, @dic)
|
76
75
|
end
|
77
76
|
|
78
77
|
def process(obj)
|
79
|
-
|
80
|
-
inc('Anzahl gesuchter Wörter')
|
81
|
-
|
82
|
-
obj = @dic.find_word(obj.form)
|
83
|
-
inc('Anzahl gefundener Wörter') unless obj.unknown?
|
84
|
-
end
|
85
|
-
|
86
|
-
forward(obj)
|
78
|
+
forward(obj.is_a?(Token) && obj.word? ? @dic.find_word(obj.form) : obj)
|
87
79
|
end
|
88
80
|
|
89
81
|
end
|
data/lib/lingo/attendee.rb
CHANGED
@@ -56,7 +56,7 @@ class Lingo
|
|
56
56
|
# was macht attendee
|
57
57
|
# - verkettung der attendees anhand von konfigurationsinformationen
|
58
58
|
# - bereitstellung von globalen und spezifischen konfigurationsinformationen
|
59
|
-
# - behandlung von bestimmten übergreifenden Kommandos, z.B. STR_CMD_TALK
|
59
|
+
# - behandlung von bestimmten übergreifenden Kommandos, z.B. STR_CMD_TALK
|
60
60
|
# - separierung und routing von kommando bzw. datenobjekten
|
61
61
|
#
|
62
62
|
# was macht die abgeleitet klasse
|
@@ -67,39 +67,28 @@ class Lingo
|
|
67
67
|
class Attendee
|
68
68
|
|
69
69
|
include Language
|
70
|
-
include Reportable
|
71
70
|
|
72
71
|
STR_CMD_TALK = 'TALK'
|
73
|
-
STR_CMD_STATUS = 'STATUS'
|
74
72
|
STR_CMD_LIR = 'LIR-FORMAT'
|
75
73
|
STR_CMD_FILE = 'FILE'
|
76
74
|
STR_CMD_EOL = 'EOL'
|
77
75
|
STR_CMD_RECORD = 'RECORD'
|
78
76
|
STR_CMD_EOF = 'EOF'
|
79
77
|
|
80
|
-
STA_NUM_COMMANDS = 'Received Commands'
|
81
|
-
STA_NUM_OBJECTS = 'Received Objects '
|
82
|
-
STA_TIM_COMMANDS = 'Time to control '
|
83
|
-
STA_TIM_OBJECTS = 'Time to process '
|
84
|
-
|
85
78
|
DEFAULT_SKIP = [TA_PUNCTUATION, TA_OTHER].join(',')
|
86
79
|
|
87
80
|
def initialize(config, lingo)
|
88
|
-
@lingo = lingo
|
89
|
-
|
90
|
-
init_reportable
|
81
|
+
@lingo, @config, @subscriber = lingo, config, []
|
91
82
|
|
92
83
|
# Make sure config exists
|
93
84
|
lingo.dictionary_config
|
94
85
|
|
95
|
-
@config, @subscriber = config, []
|
96
|
-
|
97
86
|
init if self.class.method_defined?(:init)
|
98
87
|
|
99
88
|
@can_control = self.class.method_defined?(:control)
|
100
89
|
@can_process = self.class.method_defined?(:process)
|
101
90
|
|
102
|
-
@skip_command
|
91
|
+
@skip_command = false
|
103
92
|
end
|
104
93
|
|
105
94
|
def add_subscriber(subscriber)
|
@@ -107,28 +96,17 @@ class Lingo
|
|
107
96
|
end
|
108
97
|
|
109
98
|
def listen(obj)
|
110
|
-
|
111
|
-
@can_process ? stat_timer(:objects) { process(obj) } : forward(obj)
|
112
|
-
else
|
99
|
+
if obj.is_a?(AgendaItem)
|
113
100
|
args = obj.to_a
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
nil
|
119
|
-
when STR_CMD_STATUS
|
120
|
-
report_time
|
121
|
-
report_status
|
122
|
-
|
123
|
-
forward(*args)
|
124
|
-
else
|
125
|
-
forward(*args) unless skip_command!
|
126
|
-
end
|
101
|
+
control(*args) if @can_control
|
102
|
+
forward(*args) unless obj.cmd == STR_CMD_TALK || skip_command!
|
103
|
+
else
|
104
|
+
@can_process ? process(obj) : forward(obj)
|
127
105
|
end
|
128
106
|
end
|
129
107
|
|
130
108
|
def talk(obj)
|
131
|
-
|
109
|
+
@subscriber.each { |attendee| attendee.listen(obj) }
|
132
110
|
end
|
133
111
|
|
134
112
|
private
|
@@ -138,77 +116,6 @@ class Lingo
|
|
138
116
|
g && (block_given? ? !yield(w) : w.unknown?) ? g.find_compound(f) : w
|
139
117
|
end
|
140
118
|
|
141
|
-
def report_on(cmd, *rep)
|
142
|
-
rep.each { |r| r.report.each { |q| set(*q) } } if cmd == STR_CMD_STATUS
|
143
|
-
end
|
144
|
-
|
145
|
-
def sta_for(key)
|
146
|
-
%w[NUM TIM].map { |i| self.class.const_get("STA_#{i}_#{key.upcase}") }
|
147
|
-
end
|
148
|
-
|
149
|
-
def stat_timer(key)
|
150
|
-
n, t = sta_for(key)
|
151
|
-
inc(n)
|
152
|
-
|
153
|
-
return yield unless @lingo.report_time
|
154
|
-
|
155
|
-
@timer = Time.now.to_i
|
156
|
-
res = yield
|
157
|
-
add(t, Time.now.to_i - @timer)
|
158
|
-
res
|
159
|
-
end
|
160
|
-
|
161
|
-
def charge_timer
|
162
|
-
return yield unless @lingo.report_time
|
163
|
-
|
164
|
-
res = nil
|
165
|
-
@timer += Benchmark.realtime { res = yield }
|
166
|
-
res
|
167
|
-
end
|
168
|
-
|
169
|
-
def report_time
|
170
|
-
return unless @lingo.report_time
|
171
|
-
|
172
|
-
msg = 'Perf: %-15s ' <<
|
173
|
-
'=> %7d commands in %s (%s/cmd)' <<
|
174
|
-
', %8d objects in %s (%s/obj)'
|
175
|
-
|
176
|
-
arg = [@config['name']]
|
177
|
-
|
178
|
-
%w[commands objects].each { |k|
|
179
|
-
n, t = sta_for(k).map(&method(:get))
|
180
|
-
arg << n
|
181
|
-
|
182
|
-
arg.concat([1, n].map { |m|
|
183
|
-
s = m.zero? ? 0.0 : t / m.to_f
|
184
|
-
|
185
|
-
'%9.3f %-2s' %
|
186
|
-
if s < 0.001
|
187
|
-
[s * 1000.0 ** 2, 'µs']
|
188
|
-
elsif s < 1.0
|
189
|
-
[s * 1000.0, 'ms']
|
190
|
-
elsif s < 60.0
|
191
|
-
[s, 's']
|
192
|
-
elsif s < 60.0 ** 2
|
193
|
-
[s / 60.0, 'm']
|
194
|
-
else
|
195
|
-
[s / 60.0 ** 2, 'h']
|
196
|
-
end
|
197
|
-
})
|
198
|
-
}
|
199
|
-
|
200
|
-
warn msg % arg
|
201
|
-
end
|
202
|
-
|
203
|
-
def report_status
|
204
|
-
return unless @lingo.report_status
|
205
|
-
|
206
|
-
msg = "Attendee <%s> was connected from '%s' to '%s' reporting..."
|
207
|
-
|
208
|
-
warn msg % @config.values_at(*%w[name in out]), nil,
|
209
|
-
report.sort.map! { |k, v| " #{k} = #{v}" }, nil
|
210
|
-
end
|
211
|
-
|
212
119
|
def skip_command
|
213
120
|
@skip_command = true
|
214
121
|
end
|
@@ -221,17 +128,29 @@ class Lingo
|
|
221
128
|
talk(param ? AgendaItem.new(obj, param) : obj)
|
222
129
|
end
|
223
130
|
|
131
|
+
def flush(buffer)
|
132
|
+
buffer.each { |i| forward(i) }.clear
|
133
|
+
end
|
134
|
+
|
224
135
|
def has_key?(key)
|
225
136
|
@config && @config.has_key?(key)
|
226
137
|
end
|
227
138
|
|
228
|
-
def get_key(key, default = nodefault =
|
139
|
+
def get_key(key, default = nodefault = true)
|
229
140
|
raise MissingConfigError.new(key) if nodefault && !has_key?(key)
|
230
141
|
@config.fetch(key, default)
|
231
142
|
end
|
232
143
|
|
233
|
-
def get_array(key, default = nil,
|
234
|
-
get_key(key, default).split(SEP_RE)
|
144
|
+
def get_array(key, default = nil, method = nil)
|
145
|
+
ary = get_key(key, default).split(SEP_RE)
|
146
|
+
ary.map!(&method) if method
|
147
|
+
ary
|
148
|
+
end
|
149
|
+
|
150
|
+
def get_re(key, default = nil, standard = nil)
|
151
|
+
if value = get_key(key, default)
|
152
|
+
value == true ? standard : Regexp.new(value)
|
153
|
+
end
|
235
154
|
end
|
236
155
|
|
237
156
|
def dictionary(src, mod)
|
@@ -29,7 +29,7 @@ class Lingo
|
|
29
29
|
class BufferedAttendee < Attendee
|
30
30
|
|
31
31
|
def initialize(config, lingo)
|
32
|
-
@buffer
|
32
|
+
@buffer = []
|
33
33
|
super
|
34
34
|
end
|
35
35
|
|
@@ -47,11 +47,6 @@ class Lingo
|
|
47
47
|
obj.form if obj.is_a?(klass)
|
48
48
|
end
|
49
49
|
|
50
|
-
def forward_buffer
|
51
|
-
@inserts.sort_by!(&:first).each { |i| @buffer.insert(*i) }.clear
|
52
|
-
@buffer.each(&method(:forward)).clear
|
53
|
-
end
|
54
|
-
|
55
50
|
def forward_number_of_token(len = default = @buffer.size, punct = !default)
|
56
51
|
begin
|
57
52
|
unless @buffer.empty?
|
@@ -74,9 +69,7 @@ class Lingo
|
|
74
69
|
raise NotImplementedError
|
75
70
|
end
|
76
71
|
|
77
|
-
def control_multi(cmd
|
78
|
-
report_on(cmd, dic)
|
79
|
-
|
72
|
+
def control_multi(cmd)
|
80
73
|
if [STR_CMD_RECORD, STR_CMD_EOF].include?(cmd)
|
81
74
|
@eof_handling = true
|
82
75
|
|
data/lib/lingo/call.rb
CHANGED
@@ -28,6 +28,8 @@ class Lingo
|
|
28
28
|
|
29
29
|
class Call < self
|
30
30
|
|
31
|
+
CHANNELS = %w[stdout stderr].freeze
|
32
|
+
|
31
33
|
def initialize(args = [])
|
32
34
|
super(args, StringIO.new, StringIO.new, StringIO.new)
|
33
35
|
end
|
@@ -51,20 +53,23 @@ class Lingo
|
|
51
53
|
|
52
54
|
start
|
53
55
|
|
54
|
-
|
55
|
-
io = config.send(key)
|
56
|
-
io.
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
res.sort!
|
65
|
-
res.uniq!
|
66
|
-
end
|
56
|
+
res = CHANNELS.flat_map { |key|
|
57
|
+
io = config.send(key)
|
58
|
+
io.rewind
|
59
|
+
|
60
|
+
lines = io.readlines.each { |i| i.chomp! }
|
61
|
+
|
62
|
+
io.truncate(0)
|
63
|
+
io.rewind
|
64
|
+
|
65
|
+
lines
|
67
66
|
}
|
67
|
+
|
68
|
+
block_given? ? res.map! { |i| yield i } : begin
|
69
|
+
res.sort!
|
70
|
+
res.uniq!
|
71
|
+
res
|
72
|
+
end
|
68
73
|
end
|
69
74
|
|
70
75
|
end
|
data/lib/lingo/cli.rb
CHANGED
@@ -36,8 +36,7 @@ class Lingo
|
|
36
36
|
super.merge(
|
37
37
|
config: 'lingo.cfg',
|
38
38
|
language: 'de',
|
39
|
-
|
40
|
-
perfmon: false
|
39
|
+
profile: false
|
41
40
|
)
|
42
41
|
end
|
43
42
|
|
@@ -68,18 +67,14 @@ class Lingo
|
|
68
67
|
|
69
68
|
opts.separator ''
|
70
69
|
|
71
|
-
opts.on('-
|
72
|
-
options[:
|
73
|
-
}
|
74
|
-
|
75
|
-
opts.on('-p', '--perfmon', 'Print performance details after processing') {
|
76
|
-
options[:perfmon] = true
|
70
|
+
opts.on('-L', '--log FILE', 'Log file to print debug information to') { |log|
|
71
|
+
options[:log] = stderr.reopen(log == '-' ? stdout : File.open(log, 'a+', encoding: ENC))
|
77
72
|
}
|
78
73
|
|
79
74
|
opts.separator ''
|
80
75
|
|
81
|
-
opts.on('-
|
82
|
-
options[:
|
76
|
+
opts.on('-P', '--profile PATH', 'Print profiling results') { |profile|
|
77
|
+
options[:profile] = profile == '-' ? stdout : profile
|
83
78
|
}
|
84
79
|
end
|
85
80
|
|