lingo 1.9.0.pre1 → 1.9.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.
- checksums.yaml +4 -4
- data/ChangeLog +18 -7
- data/README +6 -8
- data/Rakefile +5 -5
- data/dict/en/lingo-dic.txt +52625 -15693
- data/lang/en.lang +2 -2
- data/lib/lingo.rb +15 -3
- data/lib/lingo/array_utils.rb +39 -0
- data/lib/lingo/attendee.rb +1 -3
- data/lib/lingo/attendee/multi_worder.rb +4 -2
- data/lib/lingo/attendee/sequencer.rb +122 -73
- data/lib/lingo/attendee/text_writer.rb +4 -6
- data/lib/lingo/attendee/vector_filter.rb +5 -5
- data/lib/lingo/cli.rb +20 -2
- data/lib/lingo/config.rb +4 -3
- data/lib/lingo/ctl.rb +2 -20
- data/lib/lingo/ctl/analysis.rb +3 -5
- data/lib/lingo/ctl/files.rb +3 -3
- data/lib/lingo/database.rb +26 -25
- data/lib/lingo/database/crypter.rb +10 -6
- data/lib/lingo/database/source.rb +72 -25
- data/lib/lingo/database/source/key_value.rb +12 -8
- data/lib/lingo/database/source/multi_key.rb +11 -9
- data/lib/lingo/database/source/multi_value.rb +10 -8
- data/lib/lingo/database/source/single_word.rb +10 -6
- data/lib/lingo/database/source/word_class.rb +43 -14
- data/lib/lingo/debug.rb +2 -2
- data/lib/lingo/error.rb +21 -5
- data/lib/lingo/filter.rb +1 -1
- data/lib/lingo/language.rb +21 -21
- data/lib/lingo/language/grammar.rb +4 -2
- data/lib/lingo/language/lexical_hash.rb +2 -14
- data/lib/lingo/language/word.rb +1 -5
- data/lib/lingo/text_utils.rb +113 -20
- data/lib/lingo/version.rb +1 -1
- data/test/attendee/ts_sequencer.rb +286 -32
- data/test/attendee/ts_text_reader.rb +4 -4
- data/test/attendee/ts_text_writer.rb +19 -5
- data/test/test_helper.rb +2 -0
- data/test/ts_database.rb +213 -14
- metadata +36 -24
data/lib/lingo/cli.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# Lingo -- A full-featured automatic indexing system #
|
7
7
|
# #
|
8
8
|
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
-
# Copyright (C) 2007-
|
9
|
+
# Copyright (C) 2007-2016 John Vorhauer, Jens Wille #
|
10
10
|
# #
|
11
11
|
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
12
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -64,7 +64,8 @@ class Lingo
|
|
64
64
|
opts.separator
|
65
65
|
|
66
66
|
opts.option(:log__FILE, :L, 'Log file to print debug information to') { |log|
|
67
|
-
options[:log] = stderr.reopen(
|
67
|
+
options[:log] = stderr.reopen(
|
68
|
+
log == '-' ? stdout : File.open(log, 'a+', encoding: ENCODING))
|
68
69
|
}
|
69
70
|
|
70
71
|
opts.separator
|
@@ -76,4 +77,21 @@ class Lingo
|
|
76
77
|
|
77
78
|
end
|
78
79
|
|
80
|
+
def self.CLI(args, extra = nil, &block)
|
81
|
+
opt, req = args.partition { |arg| arg.sub!(/\?\z/, '') }
|
82
|
+
|
83
|
+
unless (n = ARGV.size - req.size) >= 0 && n <= opt.size
|
84
|
+
msg = "Usage: #{$0}#{args.map { |arg| [' ', arg].zip(
|
85
|
+
opt.include?(arg) ? %w[[ ]] : %w[< >]).join }.join}"
|
86
|
+
|
87
|
+
abort Array(extra).unshift(msg).join("\n\n")
|
88
|
+
end
|
89
|
+
|
90
|
+
Object.new.extend(TextUtils).instance_eval(&block)
|
91
|
+
rescue LingoError => err
|
92
|
+
abort err.to_s
|
93
|
+
ensure
|
94
|
+
ObjectSpace.each_object(Zlib::GzipWriter, &:close)
|
95
|
+
end
|
96
|
+
|
79
97
|
end
|
data/lib/lingo/config.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# Lingo -- A full-featured automatic indexing system #
|
7
7
|
# #
|
8
8
|
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
-
# Copyright (C) 2007-
|
9
|
+
# Copyright (C) 2007-2016 John Vorhauer, Jens Wille #
|
10
10
|
# #
|
11
11
|
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
12
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -53,7 +53,7 @@ class Lingo
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
attr_reader :
|
56
|
+
attr_reader :lang_file, :config_file
|
57
57
|
|
58
58
|
def to_h
|
59
59
|
{ 'version' => VERSION }.merge(@opts)
|
@@ -128,7 +128,8 @@ class Lingo
|
|
128
128
|
file = Lingo.find(type, @opts[key]) { quit }
|
129
129
|
instance_variable_set("@#{type}_file", file)
|
130
130
|
|
131
|
-
File.open(file, encoding:
|
131
|
+
File.open(file, encoding: ENCODING) { |f|
|
132
|
+
@opts.update(SafeYAML.load(f)) }
|
132
133
|
rescue Psych::SyntaxError => err
|
133
134
|
err.message << " (in #{file})"
|
134
135
|
raise
|
data/lib/lingo/ctl.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# Lingo -- A full-featured automatic indexing system #
|
7
7
|
# #
|
8
8
|
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
-
# Copyright (C) 2007-
|
9
|
+
# Copyright (C) 2007-2016 John Vorhauer, Jens Wille #
|
10
10
|
# #
|
11
11
|
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
12
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -31,6 +31,7 @@ class Lingo
|
|
31
31
|
module Ctl
|
32
32
|
|
33
33
|
extend self
|
34
|
+
extend TextUtils
|
34
35
|
|
35
36
|
PROG, VERSION, OPTWIDTH = $0, '0.0.3', 21
|
36
37
|
PROGNAME, OPTIONS = File.basename(PROG), {}
|
@@ -120,25 +121,6 @@ Usage: #{PROG} <command> [arguments] [options]
|
|
120
121
|
usage('Too many arguments.') unless ARGV.empty?
|
121
122
|
end
|
122
123
|
|
123
|
-
def overwrite?(target, unlink = false)
|
124
|
-
!File.exist?(target) || if agree?("#{target} already exists. Overwrite?")
|
125
|
-
File.unlink(target) if unlink
|
126
|
-
true
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def agree?(msg)
|
131
|
-
print "#{msg} (y/n) [n]: "
|
132
|
-
|
133
|
-
case answer = $stdin.gets.chomp
|
134
|
-
when /\Ano?\z/i, '' then nil
|
135
|
-
when /\Ay(?:es)?\z/i then true
|
136
|
-
else puts 'Please enter "yes" or "no".'; agree?(msg)
|
137
|
-
end
|
138
|
-
rescue Interrupt
|
139
|
-
abort ''
|
140
|
-
end
|
141
|
-
|
142
124
|
end
|
143
125
|
|
144
126
|
def self.ctl
|
data/lib/lingo/ctl/analysis.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# Lingo -- A full-featured automatic indexing system #
|
7
7
|
# #
|
8
8
|
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
-
# Copyright (C) 2007-
|
9
|
+
# Copyright (C) 2007-2016 John Vorhauer, Jens Wille #
|
10
10
|
# #
|
11
11
|
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
12
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -24,8 +24,6 @@
|
|
24
24
|
###############################################################################
|
25
25
|
#++
|
26
26
|
|
27
|
-
require 'csv'
|
28
|
-
|
29
27
|
class Lingo
|
30
28
|
|
31
29
|
module Ctl
|
@@ -97,11 +95,11 @@ class Lingo
|
|
97
95
|
File.basename(path.chomp(File.extname(path))) }.uniq.join('-'))
|
98
96
|
|
99
97
|
lambda { |key, &block| overwrite?(file = "#{name}.#{key}.csv") &&
|
100
|
-
puts("#{file}: #{Array(
|
98
|
+
puts("#{file}: #{Array(open_csv(file, 'wb', &block)).join(' / ')}") }
|
101
99
|
end
|
102
100
|
|
103
101
|
def csv_foreach(paths)
|
104
|
-
paths.each { |path|
|
102
|
+
paths.each { |path| foreach_csv(path, headers: true) { |row|
|
105
103
|
yield path, *row.values_at(*%w[string token word pattern]) } }
|
106
104
|
end
|
107
105
|
|
data/lib/lingo/ctl/files.rb
CHANGED
@@ -76,9 +76,9 @@ class Lingo
|
|
76
76
|
FileUtils.cp(source, target, verbose: true)
|
77
77
|
end
|
78
78
|
|
79
|
-
def
|
80
|
-
|
81
|
-
FileUtils.rm(
|
79
|
+
def clear(what)
|
80
|
+
target = Dir["#{find(what, false)}.*"]
|
81
|
+
FileUtils.rm(target, verbose: true) unless target.empty?
|
82
82
|
end
|
83
83
|
|
84
84
|
end
|
data/lib/lingo/database.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# Lingo -- A full-featured automatic indexing system #
|
7
7
|
# #
|
8
8
|
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
-
# Copyright (C) 2007-
|
9
|
+
# Copyright (C) 2007-2016 John Vorhauer, Jens Wille #
|
10
10
|
# #
|
11
11
|
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
12
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -45,6 +45,8 @@ class Lingo
|
|
45
45
|
KEY_REF = '*'
|
46
46
|
SYS_KEY = '~'
|
47
47
|
|
48
|
+
KEY_REF_RE = %r{\A#{Regexp.escape(KEY_REF)}(\d+)\z}o
|
49
|
+
|
48
50
|
BACKENDS = []
|
49
51
|
BACKEND_BY_EXT = {}
|
50
52
|
|
@@ -58,6 +60,25 @@ class Lingo
|
|
58
60
|
klass.class_eval('def store_ext; EXT; end', __FILE__, __LINE__) if meth
|
59
61
|
end
|
60
62
|
|
63
|
+
def backend_by_ext(file, ext = File.extname(file))
|
64
|
+
get_backend(BACKEND_BY_EXT[ext], file) or
|
65
|
+
raise BackendNotFoundError.new(file)
|
66
|
+
end
|
67
|
+
|
68
|
+
def find_backend(env = 'LINGO_BACKEND')
|
69
|
+
env && get_backend(ENV[env]) || BACKENDS.find { |name|
|
70
|
+
backend = get_backend(name, nil, true) and return backend }
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_backend(name, file = nil, relax = false)
|
74
|
+
return unless name
|
75
|
+
|
76
|
+
Object.const_get(name)
|
77
|
+
const_get("#{name}Store")
|
78
|
+
rescue TypeError, NameError => err
|
79
|
+
raise BackendNotAvailableError.new(name, file, err) unless relax
|
80
|
+
end
|
81
|
+
|
61
82
|
def open(*args, &block)
|
62
83
|
new(*args).open(&block)
|
63
84
|
end
|
@@ -75,23 +96,12 @@ class Lingo
|
|
75
96
|
FileUtils.mkdir_p(File.dirname(@stofile))
|
76
97
|
rescue SourceFileNotFoundError => err
|
77
98
|
@stofile = skip_ext = err.id
|
78
|
-
|
79
|
-
unless err.name
|
80
|
-
if name = BACKEND_BY_EXT[File.extname(@stofile)]
|
81
|
-
backend = get_backend(name, @stofile)
|
82
|
-
else
|
83
|
-
raise BackendNotFoundError.new(@stofile)
|
84
|
-
end
|
85
|
-
end
|
99
|
+
backend = self.class.backend_by_ext(@stofile) unless err.name
|
86
100
|
rescue NoWritableStoreError
|
87
101
|
backend = HashStore
|
88
102
|
end
|
89
103
|
|
90
|
-
|
91
|
-
BACKENDS.find { |name| backend = get_backend(name, nil, true) }
|
92
|
-
end
|
93
|
-
|
94
|
-
extend(@backend = backend || HashStore)
|
104
|
+
extend(@backend = backend || self.class.find_backend || HashStore)
|
95
105
|
|
96
106
|
@stofile << store_ext unless skip_ext || !respond_to?(:store_ext)
|
97
107
|
|
@@ -151,15 +161,6 @@ class Lingo
|
|
151
161
|
|
152
162
|
private
|
153
163
|
|
154
|
-
def get_backend(name, file = nil, relax = false)
|
155
|
-
return unless name
|
156
|
-
|
157
|
-
Object.const_get(name)
|
158
|
-
self.class.const_get("#{name}Store")
|
159
|
-
rescue TypeError, NameError => err
|
160
|
-
raise BackendNotAvailableError.new(name, file, err) unless relax
|
161
|
-
end
|
162
|
-
|
163
164
|
def config_hash
|
164
165
|
hashes = [config]
|
165
166
|
|
@@ -228,11 +229,11 @@ class Lingo
|
|
228
229
|
end
|
229
230
|
|
230
231
|
def _encode!(str)
|
231
|
-
str.force_encoding(
|
232
|
+
str.force_encoding(ENCODING)
|
232
233
|
end
|
233
234
|
|
234
235
|
def convert(verbose = lingo.config.stderr.tty?)
|
235
|
-
src = Source.
|
236
|
+
src = Source.from_config(config, @id)
|
236
237
|
|
237
238
|
sep, hyphenate, key_map, val_map = prepare_lex
|
238
239
|
|
@@ -6,7 +6,7 @@
|
|
6
6
|
# Lingo -- A full-featured automatic indexing system #
|
7
7
|
# #
|
8
8
|
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
-
# Copyright (C) 2007-
|
9
|
+
# Copyright (C) 2007-2016 John Vorhauer, Jens Wille #
|
10
10
|
# #
|
11
11
|
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
12
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -35,23 +35,27 @@ class Lingo
|
|
35
35
|
|
36
36
|
extend self
|
37
37
|
|
38
|
+
KEYLEN = 16
|
39
|
+
|
40
|
+
CIPHER = 'AES-128-CBC'.freeze
|
41
|
+
|
38
42
|
def digest(key)
|
39
43
|
Digest::SHA1.hexdigest(key)
|
40
44
|
end
|
41
45
|
|
42
46
|
def encode(key, val)
|
43
|
-
[digest(key), crypt(:encrypt, key, val)]
|
47
|
+
[digest = digest(key), crypt(:encrypt, key, val, digest)]
|
44
48
|
end
|
45
49
|
|
46
50
|
def decode(key, val)
|
47
|
-
crypt(:decrypt, key, val).force_encoding(
|
51
|
+
crypt(:decrypt, key, val, digest(key)).force_encoding(ENCODING)
|
48
52
|
end
|
49
53
|
|
50
54
|
private
|
51
55
|
|
52
|
-
def crypt(method, key, val)
|
53
|
-
cipher = OpenSSL::Cipher.new(
|
54
|
-
cipher.iv = cipher.key = digest
|
56
|
+
def crypt(method, key, val, digest)
|
57
|
+
cipher = OpenSSL::Cipher.new(CIPHER).send(method)
|
58
|
+
cipher.iv = cipher.key = digest[0, KEYLEN]
|
55
59
|
cipher.update(val) + cipher.final
|
56
60
|
end
|
57
61
|
|
@@ -6,7 +6,7 @@
|
|
6
6
|
# Lingo -- A full-featured automatic indexing system #
|
7
7
|
# #
|
8
8
|
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
-
# Copyright (C) 2007-
|
9
|
+
# Copyright (C) 2007-2016 John Vorhauer, Jens Wille #
|
10
10
|
# #
|
11
11
|
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
12
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -49,30 +49,57 @@ class Lingo
|
|
49
49
|
|
50
50
|
class Source
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
LEXICAL_SEPARATOR = '#'.freeze
|
53
|
+
|
54
|
+
DEFAULT_SEPARATOR = nil
|
55
|
+
|
56
|
+
DEFAULT_DEF_WC = nil
|
57
|
+
|
58
|
+
MAX_LENGTH = 4096
|
59
|
+
|
60
|
+
class << self
|
61
|
+
|
62
|
+
def from_id(id, lingo)
|
63
|
+
from_config(lingo.database_config(id), id)
|
64
|
+
end
|
65
|
+
|
66
|
+
def from_config(config, id = nil)
|
67
|
+
format = config.fetch('txt-format', 'key_value')
|
68
|
+
Lingo.get_const(format, self).new(config['name'], config, id)
|
69
|
+
end
|
70
|
+
|
71
|
+
def lexicals(val, sep = LEXICAL_SEPARATOR, ref = KEY_REF_RE)
|
72
|
+
val.map { |str|
|
73
|
+
str =~ ref ? $1.to_i : begin
|
74
|
+
k, *w = str.split(sep)
|
75
|
+
Language::Lexical.new(k.strip, w)
|
76
|
+
end
|
77
|
+
}.uniq if val
|
78
|
+
end
|
79
|
+
|
54
80
|
end
|
55
81
|
|
56
82
|
attr_reader :pos
|
57
83
|
|
58
|
-
def initialize(
|
59
|
-
@config =
|
84
|
+
def initialize(name = nil, config = {}, id = nil)
|
85
|
+
@config = config
|
60
86
|
|
61
|
-
|
87
|
+
src_file = Lingo.find(:dict, name, relax: true) if name
|
62
88
|
|
63
|
-
|
64
|
-
Lingo.find(:store,
|
89
|
+
rej_file = begin
|
90
|
+
Lingo.find(:store, src_file) << '.rev'
|
65
91
|
rescue NoWritableStoreError, SourceFileNotFoundError
|
66
|
-
end
|
92
|
+
end if id && src_file
|
67
93
|
|
68
|
-
@src = Pathname.new(
|
69
|
-
@rej = Pathname.new(
|
94
|
+
@src = Pathname.new(src_file) if src_file
|
95
|
+
@rej = Pathname.new(rej_file) if rej_file
|
70
96
|
|
71
|
-
raise SourceFileNotFoundError.new(name, id)
|
97
|
+
raise id ? SourceFileNotFoundError.new(name, id) :
|
98
|
+
FileNotFoundError.new(name) if name && !@src.exist?
|
72
99
|
|
73
|
-
@
|
100
|
+
@sep = config.fetch('separator', self.class::DEFAULT_SEPARATOR)
|
101
|
+
@def = config.fetch('def-wc', self.class::DEFAULT_DEF_WC)
|
74
102
|
@def = @def.downcase if @def
|
75
|
-
@sep = @config['separator']
|
76
103
|
|
77
104
|
@wrd = "(?:#{Language::Char::ANY})+"
|
78
105
|
@pat = /^#{@wrd}$/
|
@@ -85,33 +112,47 @@ class Lingo
|
|
85
112
|
end
|
86
113
|
|
87
114
|
def each
|
88
|
-
|
115
|
+
return enum_for(__method__) unless block_given?
|
116
|
+
each_line { |line, key, val| yield parse_line(line, key, val) }
|
117
|
+
end
|
118
|
+
|
119
|
+
def each_line
|
120
|
+
return enum_for(__method__) unless block_given?
|
121
|
+
|
122
|
+
rej_file = @rej.open('w', encoding: ENCODING) if @rej
|
89
123
|
|
90
|
-
@src.each_line($/, encoding:
|
124
|
+
@src.each_line($/, encoding: ENCODING) { |line|
|
91
125
|
@pos += length = line.bytesize
|
92
126
|
|
93
127
|
line.strip!
|
94
128
|
next if line.empty? || line.start_with?('#')
|
95
129
|
|
96
|
-
line.
|
97
|
-
|
98
|
-
|
99
|
-
if length < 4096 && line =~ @pat
|
100
|
-
yield convert_line(line, $1, $2)
|
130
|
+
if length < MAX_LENGTH && line.replace(Unicode.downcase(line)) =~ @pat
|
131
|
+
yield line, $1, $2
|
101
132
|
else
|
102
133
|
@rej_cnt += 1
|
103
|
-
|
134
|
+
rej_file.puts(line) if rej_file
|
104
135
|
end
|
105
136
|
}
|
106
137
|
|
107
138
|
self
|
108
139
|
ensure
|
109
|
-
if
|
110
|
-
|
111
|
-
@rej.delete if @rej.size
|
140
|
+
if rej_file
|
141
|
+
rej_file.close
|
142
|
+
@rej.delete if @rej.size.zero?
|
112
143
|
end
|
113
144
|
end
|
114
145
|
|
146
|
+
def each_lexical
|
147
|
+
return enum_for(__method__) unless block_given?
|
148
|
+
each { |key, val| yield key, self.class.lexicals(val) }
|
149
|
+
end
|
150
|
+
|
151
|
+
def each_dump(*args)
|
152
|
+
return enum_for(__method__, *args) unless block_given?
|
153
|
+
each_lexical { |key, val| yield dump_line(key, val, *args) }
|
154
|
+
end
|
155
|
+
|
115
156
|
def set(db, key, val)
|
116
157
|
db[key] = val
|
117
158
|
end
|
@@ -120,6 +161,12 @@ class Lingo
|
|
120
161
|
[@rej_cnt, @rej]
|
121
162
|
end
|
122
163
|
|
164
|
+
private
|
165
|
+
|
166
|
+
def lexical(*args)
|
167
|
+
args.join(LEXICAL_SEPARATOR)
|
168
|
+
end
|
169
|
+
|
123
170
|
end
|
124
171
|
|
125
172
|
end
|
@@ -6,7 +6,7 @@
|
|
6
6
|
# Lingo -- A full-featured automatic indexing system #
|
7
7
|
# #
|
8
8
|
# Copyright (C) 2005-2007 John Vorhauer #
|
9
|
-
# Copyright (C) 2007-
|
9
|
+
# Copyright (C) 2007-2016 John Vorhauer, Jens Wille #
|
10
10
|
# #
|
11
11
|
# Lingo is free software; you can redistribute it and/or modify it under the #
|
12
12
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -39,17 +39,21 @@ class Lingo
|
|
39
39
|
|
40
40
|
class KeyValue < self
|
41
41
|
|
42
|
-
DEFAULT_SEPARATOR = '*'
|
42
|
+
DEFAULT_SEPARATOR = '*'.freeze
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
DEFAULT_DEF_WC = Language::LA_UNKNOWN
|
45
|
+
|
46
|
+
def initialize(*)
|
47
|
+
super
|
48
|
+
@pat = /^(#{@wrd})#{Regexp.escape(@sep)}(#{@wrd})$/
|
47
49
|
end
|
48
50
|
|
49
|
-
|
51
|
+
def parse_line(line, key, val)
|
52
|
+
[key.strip, [lexical(val.strip, @def)]]
|
53
|
+
end
|
50
54
|
|
51
|
-
def
|
52
|
-
|
55
|
+
def dump_line(key, val, sep = @sep, *)
|
56
|
+
val.map(&:form).unshift(key).join(sep)
|
53
57
|
end
|
54
58
|
|
55
59
|
end
|