lingo 1.8.6 → 1.8.7

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +40 -4
  3. data/README +22 -51
  4. data/Rakefile +3 -17
  5. data/config/lingo.cfg +24 -15
  6. data/config/lir.cfg +25 -16
  7. data/dict/de/test_muh.txt +6 -0
  8. data/dict/en/lingo-dic.txt +2 -3
  9. data/lang/de.lang +10 -9
  10. data/lang/en.lang +1 -1
  11. data/lib/lingo.rb +4 -4
  12. data/lib/lingo/attendee.rb +27 -7
  13. data/lib/lingo/attendee/analysis_filter.rb +81 -0
  14. data/lib/lingo/attendee/debug_filter.rb +42 -0
  15. data/lib/lingo/attendee/debugger.rb +2 -11
  16. data/lib/lingo/attendee/decomposer.rb +6 -3
  17. data/lib/lingo/attendee/formatter.rb +6 -6
  18. data/lib/lingo/attendee/hal_filter.rb +94 -0
  19. data/lib/lingo/attendee/lsi_filter.rb +99 -0
  20. data/lib/lingo/attendee/multi_worder.rb +69 -43
  21. data/lib/lingo/attendee/sequencer.rb +32 -19
  22. data/lib/lingo/attendee/synonymer.rb +2 -2
  23. data/lib/lingo/attendee/text_reader.rb +63 -92
  24. data/lib/lingo/attendee/text_writer.rb +12 -21
  25. data/lib/lingo/attendee/tokenizer.rb +32 -21
  26. data/lib/lingo/attendee/variator.rb +3 -3
  27. data/lib/lingo/attendee/vector_filter.rb +7 -9
  28. data/lib/lingo/attendee/word_searcher.rb +3 -3
  29. data/lib/lingo/buffered_attendee.rb +3 -36
  30. data/lib/lingo/config.rb +1 -1
  31. data/lib/lingo/ctl.rb +7 -155
  32. data/lib/lingo/ctl/analysis.rb +136 -0
  33. data/lib/lingo/ctl/files.rb +86 -0
  34. data/lib/lingo/ctl/other.rb +140 -0
  35. data/lib/lingo/database.rb +64 -60
  36. data/lib/lingo/database/crypter.rb +7 -5
  37. data/lib/lingo/error.rb +5 -4
  38. data/lib/lingo/language.rb +13 -5
  39. data/lib/lingo/language/grammar.rb +13 -7
  40. data/lib/lingo/language/token.rb +6 -0
  41. data/lib/lingo/language/word.rb +23 -36
  42. data/lib/lingo/language/word_form.rb +5 -1
  43. data/lib/lingo/srv.rb +2 -2
  44. data/lib/lingo/text_utils.rb +96 -0
  45. data/lib/lingo/version.rb +1 -1
  46. data/lib/lingo/web/views/index.erb +1 -1
  47. data/test/attendee/ts_decomposer.rb +23 -5
  48. data/test/attendee/ts_multi_worder.rb +66 -0
  49. data/test/attendee/ts_sequencer.rb +28 -4
  50. data/test/attendee/ts_text_reader.rb +20 -0
  51. data/test/attendee/ts_tokenizer.rb +20 -0
  52. data/test/attendee/ts_variator.rb +1 -1
  53. data/test/attendee/ts_word_searcher.rb +39 -3
  54. data/test/lir3.txt +12 -0
  55. data/test/ref/artikel.non +1 -12
  56. data/test/ref/artikel.seq +3 -1
  57. data/test/ref/artikel.vec +1 -0
  58. data/test/ref/artikel.vef +35 -34
  59. data/test/ref/artikel.ven +8 -7
  60. data/test/ref/artikel.ver +34 -33
  61. data/test/ref/artikel.vet +2573 -2563
  62. data/test/ref/lir.non +77 -78
  63. data/test/ref/lir.seq +9 -7
  64. data/test/ref/lir.syn +1 -1
  65. data/test/ref/lir.vec +41 -41
  66. data/test/ref/lir.vef +210 -210
  67. data/test/ref/lir.ven +46 -46
  68. data/test/ref/lir.ver +72 -72
  69. data/test/ref/lir.vet +329 -329
  70. data/test/ts_database.rb +166 -62
  71. data/test/ts_language.rb +23 -23
  72. metadata +53 -34
  73. data/lib/lingo/attendee/dehyphenizer.rb +0 -120
  74. data/lib/lingo/attendee/noneword_filter.rb +0 -115
  75. data/test/attendee/ts_noneword_filter.rb +0 -15
@@ -0,0 +1,136 @@
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-2015 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 'csv'
28
+
29
+ class Lingo
30
+
31
+ module Ctl
32
+
33
+ { stats: [:s, 'Extract statistics from analysis file(s)', 'path...'],
34
+ trans: [:t, 'Transpose columns and rows of analysis file(s)', 'path...']
35
+ }.each { |n, (s, *a)| cmd("analysis#{n}", "a#{s}", *a) }
36
+
37
+ private
38
+
39
+ def do_analysisstats
40
+ require 'nuggets/array/histogram'
41
+
42
+ paths, write = paths_write
43
+ stats, patts = Hash.array(1), Hash.array(1)
44
+
45
+ csv_foreach(paths) { |path, _, token, word, pattern|
46
+ token ? stats[:tokens][path] << token : word ? begin
47
+ stats[:words][path] << word
48
+ patts[word][path] << pattern if pattern
49
+ end : nil
50
+ }
51
+
52
+ stats.each { |k, h| write.(k) { |csv|
53
+ csv << ['file', *c = columns(g = histograms(h))]
54
+ histograms_to_csv(csv, c, g)
55
+ h.values.map(&:size)
56
+ } }
57
+
58
+ write.(:patterns) { |csv|
59
+ csv << ['file', 'word', *c = columns(patts, :values)]
60
+ patts.sort.each { |k, h| histograms_to_csv(csv, c, histograms(h), k) }
61
+ c.size - 1
62
+ }
63
+ end
64
+
65
+ def do_analysistrans
66
+ paths, write = paths_write
67
+
68
+ rows, comm, more, less = Hash.array(1), {}, Hash.array(1), Hash.array(1)
69
+
70
+ csv_foreach(paths) { |path, string, token, word, _|
71
+ rows[token || word][path] << string }
72
+
73
+ c = rows.keys.sort.each { |k|
74
+ a = (h = rows[k]).first.last
75
+
76
+ paths.size == 1 ? comm[k] = a : begin
77
+ comm[k] = a & (o = h.drop(1)).flat_map(&:last)
78
+ o.each { |path, b| more[path][k] = b - a; less[path][k] = a - b }
79
+ end
80
+ }
81
+
82
+ rows.clear
83
+
84
+ write.(:transpose) { |csv| transpose_csv(csv, c, comm) }
85
+
86
+ { transmore: more, transless: less }.each { |k, v| v.each { |path, h|
87
+ csv_writer(path, *paths).(k) { |csv| transpose_csv(csv, c, h) } } }
88
+ end
89
+
90
+ def paths_write
91
+ ARGV.empty? ? missing_arg(:path) : [a = ARGV.each { |x|
92
+ abort "No such file: #{x}" unless File.exist?(x) }, csv_writer(*a)]
93
+ end
94
+
95
+ def csv_writer(*paths)
96
+ name = File.join(File.dirname(paths.first), paths.map { |path|
97
+ File.basename(path.chomp(File.extname(path))) }.uniq.join('-'))
98
+
99
+ lambda { |key, &block| overwrite?(file = "#{name}.#{key}.csv") &&
100
+ puts("#{file}: #{Array(CSV.open(file, 'wb', &block)).join(' / ')}") }
101
+ end
102
+
103
+ def csv_foreach(paths)
104
+ paths.each { |path| CSV.foreach(path, headers: true) { |row|
105
+ yield path, *row.values_at(*%w[string token word pattern]) } }
106
+ end
107
+
108
+ def columns(hash, map = :keys)
109
+ hash.values.map(&map).flatten.uniq.sort
110
+ end
111
+
112
+ def histograms(hash)
113
+ hash.each_with_object({}) { |(k, v), h|
114
+ h[k] = v.histogram.tap { |x| x.default = nil } }
115
+ end
116
+
117
+ def histograms_to_csv(csv, columns, histograms, *args)
118
+ histograms.each { |key, histogram|
119
+ others = histograms.values_at(*histograms.keys - [key])
120
+ others = [{}] if others.empty?
121
+
122
+ csv << args.dup.unshift(key).concat(columns.map { |header|
123
+ value = histogram[header] or next
124
+ value if others.any? { |other| other[header] != value }
125
+ })
126
+ }
127
+ end
128
+
129
+ def transpose_csv(csv, columns, rows)
130
+ csv << columns; values = rows.values_at(*columns); rows.clear
131
+ values.map(&:size).max.times { |i| csv << values.map { |v| v[i] } }
132
+ end
133
+
134
+ end
135
+
136
+ end
@@ -0,0 +1,86 @@
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-2015 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 Ctl
30
+
31
+ { config: %w[c configuration],
32
+ lang: %w[l language],
33
+ dict: %w[d dictionary dictionaries],
34
+ store: %w[s store],
35
+ sample: %w[e sample\ text\ file]
36
+ }.each { |n, (s, q, r)|
37
+ t = n == :store
38
+
39
+ cmd([:list, :l, n], s, "List available #{r || "#{q}s"}", '[name...]') if !t
40
+ cmd([:find, :f, n], s, "Find #{q} in Lingo search path", 'name')
41
+ cmd([:copy, :c, n], s, "Copy #{q} to local Lingo directory", 'name') if !t
42
+ cmd([:clear, :c, n], s, 'Remove store files to force rebuild', 'name') if t
43
+ }
44
+
45
+ private
46
+
47
+ def list(what, doit = true)
48
+ names = Regexp.union(*ARGV.empty? ? '' : ARGV)
49
+
50
+ Lingo.list(what, path: path_for_scope).select { |file|
51
+ File.basename(file) =~ names ? doit ? puts(file) : true : false
52
+ }
53
+ end
54
+
55
+ def find(what, doit = true, path = path_for_scope)
56
+ name = ARGV.shift or missing_arg(:name)
57
+ no_args
58
+
59
+ file = Lingo.find(what, name, path: path) { |err| usage(err) }
60
+ doit ? puts(file) : file
61
+ end
62
+
63
+ def copy(what)
64
+ usage('Source and target are the same.') if OPTIONS[:scope] == :local
65
+
66
+ local_path = path_for_scope(:local)
67
+
68
+ source = find(what, false, path_for_scope || Lingo::PATH - local_path)
69
+ target = File.expand_path(Lingo.basepath(what, source), local_path[0])
70
+
71
+ usage('Source and target are the same.') if source == target
72
+
73
+ return unless overwrite?(target)
74
+
75
+ FileUtils.mkdir_p(File.dirname(target))
76
+ FileUtils.cp(source, target, verbose: true)
77
+ end
78
+
79
+ def do_clearstore
80
+ store = Dir["#{find(:store, false)}.*"]
81
+ FileUtils.rm(store, verbose: true) unless store.empty?
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,140 @@
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-2015 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 'zip'
28
+ Zip.unicode_names = true
29
+
30
+ class Lingo
31
+
32
+ module Ctl
33
+
34
+ { demo: [:d, 'Initialize demo directory', '[path]', 'current directory'],
35
+ archive: [:a, 'Create archive of directory', '[path]', 'current directory'],
36
+ rackup: [:r, 'Print path to rackup file', 'name'],
37
+ path: [:p, 'Print search path for dictionaries and configurations'],
38
+ help: [:h, 'Print help for available commands', '[command...]'],
39
+ version: [:v, 'Print Lingo version number']
40
+ }.each { |n, (s, *a)| cmd(n.to_s, s.to_s, *a) }
41
+
42
+ private
43
+
44
+ def do_archive
45
+ OPTIONS.update(path: ARGV.shift, scope: :local)
46
+ no_args
47
+
48
+ source = File.expand_path(path_for_scope.first)
49
+ target = "#{source}.zip"
50
+
51
+ abort "No such directory: #{source}" unless Dir.exist?(source)
52
+
53
+ return unless overwrite?(target, true)
54
+
55
+ base, name = File.split(source)
56
+
57
+ Dir.chdir(base) {
58
+ Zip::File.open(target, Zip::File::CREATE) { |zipfile|
59
+ Dir[File.join(name, '**', '*')].each { |file|
60
+ zipfile.add(file, file)
61
+ }
62
+ }
63
+ }
64
+
65
+ puts "Directory successfully archived at `#{target}'."
66
+ end
67
+
68
+ def do_demo
69
+ OPTIONS.update(path: ARGV.shift, scope: :system)
70
+ no_args
71
+
72
+ path = path_for_scope(:local).first
73
+
74
+ copy_list(:config) { |i| !File.basename(i).start_with?('test') }
75
+ copy_list(:lang)
76
+ copy_list(:dict) { |i| File.basename(i).start_with?('user') }
77
+ copy_list(:sample)
78
+
79
+ puts "Demo directory successfully initialized at `#{path}'."
80
+ end
81
+
82
+ def do_rackup(doit = true)
83
+ name = ARGV.shift or missing_arg(:name)
84
+ no_args
85
+
86
+ require 'lingo/app'
87
+
88
+ if file = Lingo::App.rackup(name)
89
+ doit ? puts(file) : file
90
+ else
91
+ usage("Invalid app name `#{name.inspect}'.")
92
+ end
93
+ end
94
+
95
+ def do_path
96
+ no_args
97
+ puts path_for_scope || PATH
98
+ end
99
+
100
+ def do_help(opts = nil)
101
+ msg = opts ? [opts, 'Commands:'] : []
102
+
103
+ args = ARGV unless opts || ARGV.empty?
104
+
105
+ aliases = Hash.array
106
+ ALIASES.each { |k, v| aliases[v] << k }
107
+
108
+ COMMANDS.each { |c, (d, *e)|
109
+ a = aliases[c]
110
+ next if args && ([c, *a] & args).empty?
111
+
112
+ c = "#{c} (#{a.join(', ')})" unless a.empty?
113
+
114
+ if opts
115
+ msg << " %-#{OPTWIDTH}s %s" % [c, d]
116
+ else
117
+ msg << "#{c}" << " - #{d}"
118
+ e.each { |i| msg << " + #{i}" }
119
+ end
120
+ }
121
+
122
+ msg.empty? ? abort : abort(msg.join("\n"))
123
+ end
124
+
125
+ def do_version(doit = true)
126
+ no_args
127
+
128
+ msg = "Lingo v#{Lingo::VERSION}"
129
+ doit ? puts(msg) : msg
130
+ end
131
+
132
+ def copy_list(what)
133
+ files = list(what, false)
134
+ files.select! { |i| yield i } if block_given?
135
+ files.each { |file| ARGV.replace([file]); copy(what) }
136
+ end
137
+
138
+ end
139
+
140
+ 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-2014 John Vorhauer, Jens Wille #
9
+ # Copyright (C) 2007-2015 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 #
@@ -67,22 +67,33 @@ class Lingo
67
67
  def initialize(id, lingo)
68
68
  @id, @lingo, @config, @db = id, lingo, lingo.database_config(id), nil
69
69
 
70
- @srcfile = Lingo.find(:dict, config['name'], relax: true)
71
- @crypter = config.key?('crypt') && Crypter.new
72
-
73
- @val = Hash.nest { [] }
70
+ @val, @crypt, @srcfile = Hash.array, config.key?('crypt'),
71
+ Lingo.find(:dict, config['name'], relax: true)
74
72
 
75
73
  begin
76
74
  @stofile = Lingo.find(:store, @srcfile)
77
75
  FileUtils.mkdir_p(File.dirname(@stofile))
78
76
  rescue SourceFileNotFoundError => err
79
77
  @stofile = skip_ext = err.id
80
- backend = backend_from_file(@stofile) unless err.name
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
81
86
  rescue NoWritableStoreError
82
87
  backend = HashStore
83
88
  end
84
89
 
85
- use_backend(backend, skip_ext)
90
+ unless backend ||= get_backend(ENV['LINGO_BACKEND'])
91
+ BACKENDS.find { |name| backend = get_backend(name, nil, true) }
92
+ end
93
+
94
+ extend(@backend = backend || HashStore)
95
+
96
+ @stofile << store_ext unless skip_ext || !respond_to?(:store_ext)
86
97
 
87
98
  convert unless uptodate?
88
99
  end
@@ -131,7 +142,7 @@ class Lingo
131
142
  val.uniq!
132
143
 
133
144
  val = val.join(FLD_SEP)
134
- @crypter ? _set(*@crypter.encode(key, val)) : _set(key, val)
145
+ @crypt ? _set(*Crypter.encode(key, val)) : _set(key, val)
135
146
  end
136
147
 
137
148
  def warn(*msg)
@@ -140,26 +151,13 @@ class Lingo
140
151
 
141
152
  private
142
153
 
143
- def use_backend(backend = nil, skip_ext = false)
144
- [ENV['LINGO_BACKEND'], *BACKENDS].each { |mod|
145
- backend = get_backend(mod) and break if mod
146
- } unless backend
147
-
148
- extend(@backend = backend || HashStore)
149
-
150
- @stofile << store_ext if !skip_ext && respond_to?(:store_ext)
151
- end
152
-
153
- def get_backend(mod)
154
- self.class.const_get("#{mod}Store") if Object.const_defined?(mod)
155
- rescue TypeError, NameError
156
- end
157
-
158
- def backend_from_file(file)
159
- ext = File.extname(file)
154
+ def get_backend(name, file = nil, relax = false)
155
+ return unless name
160
156
 
161
- mod = BACKEND_BY_EXT[ext] or raise BackendNotFoundError.new(file)
162
- get_backend(mod) or raise BackendNotAvailableError.new(mod, file)
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
163
161
  end
164
162
 
165
163
  def config_hash
@@ -223,9 +221,9 @@ class Lingo
223
221
  end
224
222
 
225
223
  def _val(key)
226
- if val = _get(@crypter ? Crypter.digest(key) : key)
224
+ if val = _get(@crypt ? Crypter.digest(key) : key)
227
225
  _encode!(val)
228
- @crypter ? @crypter.decode(key, val) : val
226
+ @crypt ? Crypter.decode(key, val) : val
229
227
  end
230
228
  end
231
229
 
@@ -236,32 +234,41 @@ class Lingo
236
234
  def convert(verbose = lingo.config.stderr.tty?)
237
235
  src = Source.get(config.fetch('txt-format', 'key_value'), @id, lingo)
238
236
 
239
- sep, key_map, val_map = prepare_lex
237
+ sep, hyphenate, key_map, val_map = prepare_lex
240
238
 
241
239
  Progress.new(self, src, verbose) { |progress| create {
242
240
  src.each { |key, val|
243
241
  progress << src.pos
244
242
 
245
- if key
246
- key.chomp!('.')
247
-
248
- if sep && key.include?(sep)
249
- key = key.split(sep).map!(&key_map).join(sep)
250
- val = val.map { |v| val_map[v.split(sep)].join(sep) } if val_map
243
+ set_key(src, key, val, sep) { |keys, cnt|
244
+ key = keys.map!(&key_map).join(sep)
245
+ val = val.map { |v| val_map[v.split(sep)].join(sep) } if val_map
251
246
 
252
- if (cnt = key.count(sep)) > 2
253
- self[key.split(sep)[0, 3].join(sep)] = ["#{KEY_REF}#{cnt + 1}"]
254
- end
255
- end
256
- end
247
+ hyphenate.repeated_permutation(cnt - 1) { |h| set_key(src, keys.
248
+ zip(h).join, val, sep) unless h.uniq.size == 1 } if hyphenate
257
249
 
258
- src.set(self, key, val)
250
+ [key, val]
251
+ }
259
252
  }
260
253
 
261
254
  uptodate!
262
255
  } }
263
256
  end
264
257
 
258
+ def set_key(src, key, val, sep, len = 3)
259
+ if key
260
+ key.chomp!('.')
261
+
262
+ if sep && key.include?(sep)
263
+ keys = key.split(sep); cnt = keys.size
264
+ key, val = yield keys, cnt if block_given?
265
+ self[keys[0, len].join(sep)] = ["#{KEY_REF}#{cnt}"] if cnt > len
266
+ end
267
+ end
268
+
269
+ src.set(self, key, val)
270
+ end
271
+
265
272
  def prepare_lex
266
273
  use_lex = config['use-lex'] or return
267
274
 
@@ -275,6 +282,9 @@ class Lingo
275
282
 
276
283
  args = nil
277
284
 
285
+ wac = Language::WA_COMPOUND
286
+ lac = Language::LA_COMPOUND
287
+
278
288
  if inflect = config['inflect']
279
289
  inflect, wc = inflect == true ? %w[s e] : inflect.split(SEP_RE), 'a'
280
290
 
@@ -286,19 +296,16 @@ class Lingo
286
296
  end
287
297
  end
288
298
 
289
- [' ', lambda { |form|
299
+ [sep = ' ', config['hyphenate'] && [sep, '-'], lambda { |form|
290
300
  word = dic.find_word(form)
291
301
 
292
302
  if word.unknown?
293
- compo = gra.find_compound(form)
303
+ comp = gra.find_compound(form)
294
304
 
295
- if compo_form = compo.compo_form
296
- compo_form.form
297
- else
298
- compo.norm
299
- end
305
+ comp.attr == wac && comp.lex_form(lac) ||
306
+ (comp.identified? ? comp.lex_form : comp.form)
300
307
  else
301
- word.norm
308
+ word.identified? ? word.lex_form : word.form
302
309
  end
303
310
  }, inflect && lambda { |forms|
304
311
  inflectables = []
@@ -306,24 +313,21 @@ class Lingo
306
313
  forms.each { |form|
307
314
  word = dic.find_word(word_form = form[re])
308
315
 
309
- if word.identified? and lexical = word.get_class(wc).first
310
- inflectables << form if form == lexical.form
316
+ if word.identified? && _form = word.lex_form(wc)
317
+ inflectables << form if form == _form
311
318
  else
312
319
  unless inflectables.empty?
313
- comp = gra.find_compound(word_form) if word.unknown?
314
- word = comp.head || comp if comp && !comp.unknown?
320
+ word = gra.find_compound_head(word_form) || word if word.unknown?
315
321
 
316
- if word.attr?(*inflect)
317
- suffix = suffixes[word.genders.compact.first]
318
- inflectables.each { |lex_form| lex_form << suffix } if suffix
322
+ if word.attr?(*inflect) && suffix =
323
+ suffixes[word.genders.compact.first]
324
+ inflectables.each { |lex_form| lex_form << suffix }
319
325
  end
320
326
  end
321
327
 
322
- break
328
+ break forms
323
329
  end
324
330
  }
325
-
326
- forms
327
331
  }]
328
332
  end
329
333