verbvector 0.0.1
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.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/README.textile +96 -0
- data/Rakefile +12 -0
- data/lib/verbvector.rb +295 -0
- data/lib/verbvector/version.rb +8 -0
- data/test/fixtures/tense_list +21 -0
- data/test/test_generator.rb +135 -0
- data/verbvector.gemspec +21 -0
- metadata +64 -0
data/Gemfile
ADDED
data/README.textile
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
h1. Lingustics::Verbs::Verbvector::VerbvectorGenerator
|
2
|
+
|
3
|
+
This is, what I hope will become a burgeoning array of tools for those interested in languages and linguistics. Any given language's conjugation can be seen as a _vector_, an intersection of several _aspects_ that singularly identify the correct grammatical choice for a given utterance.
|
4
|
+
|
5
|
+
In Latin, for example, this is defined by *voice*, *mood*, *tense*, *number*, and *person*. Other languages have a more- or less-specific vector. The function of this module, therefore, is to make the exhaustive generation of these vectors simple.
|
6
|
+
|
7
|
+
h1. Usage
|
8
|
+
|
9
|
+
Usage is performed by passing a DSL into the module. The module then returns a module based on the _language_ parameter. This resultant module can be mixed-in to your class (say, *LanguageVerb*) to define all the possible method calls (for playing nice with responds_to in case *LanguageVerb* uses metaprogramming in its responses)
|
10
|
+
|
11
|
+
h1. DSL
|
12
|
+
|
13
|
+
Initial attempts at producing a verb tense generator wound up producing an incredibly confusing snarl of @eval@ statments and other oddities and wound up neither being specific enough to be easily used nor portable enough to be generally helpful.
|
14
|
+
|
15
|
+
The DSL is simple and recognizes the following statements:
|
16
|
+
|
17
|
+
h2. language :LanguageName _block_
|
18
|
+
|
19
|
+
The first statement. It takes a block that will be further processed and which contains other defining statements for the :Language. The only accepted argument is a symbol for the Language name.
|
20
|
+
|
21
|
+
h2. all_vectors :position _block_
|
22
|
+
|
23
|
+
This defines an aspect common to *all* vectors. Aspects are defined in an anonymous hash included in the block. The :position symbol is not presently used but instead stacks from left to right. This may have reason to change.
|
24
|
+
|
25
|
+
Inside the block one gives the _aspect_ as a symbol and then gives an array containing the valid specifications for that aspect.
|
26
|
+
|
27
|
+
Example:
|
28
|
+
|
29
|
+
<pre>
|
30
|
+
{
|
31
|
+
:voice => %w(active passive),
|
32
|
+
:mood => %w(indicative subjunctive imperative)
|
33
|
+
}
|
34
|
+
</pre>
|
35
|
+
|
36
|
+
h2. vectors_that Regexp _block_
|
37
|
+
|
38
|
+
This defines an aspect common to *all* vectors. Aspects are defined in an anonymous hash included in the block. Thus for all existing vectors in the vector stack that match the Regexp, this aspect/specification will be added
|
39
|
+
|
40
|
+
<pre>
|
41
|
+
{
|
42
|
+
:tense => %w(present imperfect future
|
43
|
+
perfect pastperfect futureperfect)
|
44
|
+
}
|
45
|
+
</pre>
|
46
|
+
|
47
|
+
h2. exception (:remove|:add), :branch _block_
|
48
|
+
|
49
|
+
Languages are not perfectly rational in their combinations. Occasionally you may need to manually add a vector or, more likely, prune a vector.
|
50
|
+
|
51
|
+
h1. Example
|
52
|
+
|
53
|
+
_Taken from the test/ directory_
|
54
|
+
|
55
|
+
<pre>
|
56
|
+
|
57
|
+
Lingustics::Verbs::Verbvector::VerbvectorGenerator.new do
|
58
|
+
language :Latin do
|
59
|
+
all_vectors :start_with do
|
60
|
+
{
|
61
|
+
:voice => %w(active passive),
|
62
|
+
:mood => %w(indicative subjunctive imperative)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
vectors_that /.*_indicative_mood/ do
|
66
|
+
{
|
67
|
+
:tense => %w(present imperfect future
|
68
|
+
perfect pastperfect futureperfect)
|
69
|
+
}
|
70
|
+
end
|
71
|
+
vectors_that /.*_subjunctive_mood/ do
|
72
|
+
{
|
73
|
+
:tense => %w(present imperfect
|
74
|
+
perfect pastperfect)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
vectors_that /.*_imperative_mood/ do
|
78
|
+
{
|
79
|
+
:tense => %w(present)
|
80
|
+
}
|
81
|
+
end
|
82
|
+
all_vectors :end_with do
|
83
|
+
{
|
84
|
+
:number => %w(singular plural),
|
85
|
+
:person => %w(first second third)
|
86
|
+
}
|
87
|
+
end
|
88
|
+
exception :remove, :passive_voice_imperative_mood_present_tense
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
</pre>
|
93
|
+
|
94
|
+
h1. Author
|
95
|
+
|
96
|
+
Steven G. Harms
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
#Added to get testing working
|
5
|
+
require 'rake/testtask'
|
6
|
+
Rake::TestTask.new(:test)
|
7
|
+
|
8
|
+
require "rake/rdoctask"
|
9
|
+
Rake::RDocTask.new do |rd|
|
10
|
+
rd.rdoc_files.include("lib/**/*.rb")
|
11
|
+
rd.rdoc_dir = "rdoc"
|
12
|
+
end
|
data/lib/verbvector.rb
ADDED
@@ -0,0 +1,295 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
+
require "verbvector"
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
# Generalized module for handling lingustics processing
|
6
|
+
module Linguistics
|
7
|
+
# Generalized module related to the conjugation of verbs
|
8
|
+
module Verbs
|
9
|
+
# Each language's specific verb conjugation can be described in terms of a
|
10
|
+
# verbal vector, the particular intersection of its voice, tense, number,
|
11
|
+
# and person.
|
12
|
+
module Verbvector
|
13
|
+
# A class designed to generate a module that can be mixed in into a
|
14
|
+
# given type of verb (French, Latin, Spanish). I'm not trying to be
|
15
|
+
# obscure, but I'm trying to be very generalizable.
|
16
|
+
class VerbvectorGenerator
|
17
|
+
attr_reader :language, :aspect_list, :vector_list, :cluster_methods,
|
18
|
+
:respondable_methods
|
19
|
+
|
20
|
+
# Class methods go here
|
21
|
+
class << self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Initialization
|
25
|
+
#
|
26
|
+
# Takes the descriptive block of the tense structure in a DSL format
|
27
|
+
def initialize(&b)
|
28
|
+
@aspect_list = []
|
29
|
+
@vector_list = []
|
30
|
+
@respondable_methods = []
|
31
|
+
@cluster_methods = {}
|
32
|
+
@language = ""
|
33
|
+
|
34
|
+
# Let's remember the difference between instance_ and class_eval.
|
35
|
+
#
|
36
|
+
# "class_eval sets things up as if you were in the body of a class
|
37
|
+
# definition, so method definitions will define instance methods:"
|
38
|
+
#
|
39
|
+
# "instance_eval on a class acts as if you were working inside the
|
40
|
+
# singleton class of self. Therefore, any methods you define will
|
41
|
+
# become class methods." - prog ruby 1.9 v3, prag programmers p.388
|
42
|
+
#
|
43
|
+
# As such, instance_eval here is about to apply this eval to the
|
44
|
+
# eigenclass. That means any instance of this class will have this
|
45
|
+
# method.
|
46
|
+
instance_eval &b
|
47
|
+
|
48
|
+
@aspect_list.sort!
|
49
|
+
end
|
50
|
+
|
51
|
+
# Vectors are specified at their most atomic
|
52
|
+
# Sometimes it is handy to return all the values that match
|
53
|
+
# "up to" a given aspect and then uniq'ify them
|
54
|
+
|
55
|
+
def match_vector_upto_aspect(s)
|
56
|
+
@vector_list.compact.sort.grep(/#{s}/).map{ |x|
|
57
|
+
x.sub(/(^.*#{s}).*/,%q(\1))
|
58
|
+
}.uniq
|
59
|
+
end
|
60
|
+
|
61
|
+
# Creates the anonymous module based on the contents of @vector_list.
|
62
|
+
# The method names in this module are just stubs _except_ those that
|
63
|
+
# are loaded into the @cluster_methods hash. By generating all the
|
64
|
+
# method names we allow +responds_to?+ to work as expected.
|
65
|
+
|
66
|
+
def method_extension_module
|
67
|
+
v = @vector_list
|
68
|
+
c = @cluster_methods
|
69
|
+
l = @language
|
70
|
+
r = @respondable_methods
|
71
|
+
|
72
|
+
Module.new do
|
73
|
+
# This defines instance methods on the Module
|
74
|
+
# m.instance_methods #=> [:say_foo, :say_bar, :say_bat]
|
75
|
+
|
76
|
+
# Note, you can't use @someArray in the iteration because
|
77
|
+
# self has changed to this anonymous module. Since a block
|
78
|
+
# is a binding, it has the local context (including 'v')
|
79
|
+
# bundled up with it -- despte self having changed!
|
80
|
+
# Therefore, the following works.
|
81
|
+
|
82
|
+
# Define a method for each name in vector_list.
|
83
|
+
raise("Language was not defined." ) if l.nil?
|
84
|
+
|
85
|
+
v.each do |m|
|
86
|
+
r << "#{m}"
|
87
|
+
define_method "#{l.downcase}_#{m}".to_sym do
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Write something to spit out the vectors as well.
|
92
|
+
define_method :vector_list do
|
93
|
+
return v
|
94
|
+
end
|
95
|
+
|
96
|
+
define_method :respondable_methods do
|
97
|
+
return r
|
98
|
+
end
|
99
|
+
|
100
|
+
# Spit out the clustered methods
|
101
|
+
c.each_pair do |k,v|
|
102
|
+
define_method k do
|
103
|
+
v.call
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Language takes a symbol for +l+ the language whose verb we seek to
|
111
|
+
# model. It then takes a block for the sub-specification of the verbs
|
112
|
+
# of that language.
|
113
|
+
def language(*l,&b)
|
114
|
+
return @language if (l[0].nil? and not @language.nil?)
|
115
|
+
@language = l[0]
|
116
|
+
instance_eval &b
|
117
|
+
end
|
118
|
+
|
119
|
+
# Method generates tense vectors based on aspects that are assumed to
|
120
|
+
# apply to all possible vectors. These would be seen as the most
|
121
|
+
# general aspects possible For example, while only *some* vectors are
|
122
|
+
# present tense, *all* vectors have a voice.
|
123
|
+
|
124
|
+
def all_vectors(position,&b)
|
125
|
+
# Make sure there is a block given
|
126
|
+
return unless (block_given? or yield.first)
|
127
|
+
|
128
|
+
# Sentinel condition for stopping recursive call
|
129
|
+
return @vector_list unless yield.first
|
130
|
+
|
131
|
+
# Provided that there was a block, collect the DSL hash into
|
132
|
+
# specifications
|
133
|
+
specifications = yield
|
134
|
+
|
135
|
+
# Extract the first k/v
|
136
|
+
specification = specifications.first
|
137
|
+
|
138
|
+
# Based on the key for each row, take that content and postpend
|
139
|
+
# it.to_s to the ending of each value held in the hash element value
|
140
|
+
expanded_specification = combinatorialize(specification)
|
141
|
+
|
142
|
+
# Remove the expanded value from the specifications hash
|
143
|
+
specifications.delete(specification[0])
|
144
|
+
|
145
|
+
# Keep a record of aspects we have seen
|
146
|
+
@aspect_list.push specification[0]
|
147
|
+
@aspect_list.uniq!
|
148
|
+
|
149
|
+
# If it's the first go round put the first set of values in. In
|
150
|
+
# general these should be the leftmost and theremfore most general
|
151
|
+
if @vector_list.empty?
|
152
|
+
@vector_list = expanded_specification
|
153
|
+
else
|
154
|
+
# If there's already a definition in the tens list, for each of
|
155
|
+
# the _existing_ values add the array of strings seen in
|
156
|
+
# expanded_specifications thereunto. Hold them in 'temp' and then
|
157
|
+
# set @vector_list to temp.
|
158
|
+
temp = []
|
159
|
+
@vector_list.each do |base|
|
160
|
+
expanded_specification.each do |u|
|
161
|
+
temp.push base+"_#{u}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
@vector_list = temp
|
165
|
+
end
|
166
|
+
|
167
|
+
# Recursive call, sentnel contition is at the top of the method
|
168
|
+
all_vectors(position) do
|
169
|
+
specifications
|
170
|
+
end
|
171
|
+
|
172
|
+
instance_eval &b
|
173
|
+
end
|
174
|
+
|
175
|
+
# Method appends vector definitions _if_ the +condition+ (a RegEx) is satisfied
|
176
|
+
def vectors_that(condition,&b)
|
177
|
+
matching_stems = @vector_list.grep condition
|
178
|
+
temp = []
|
179
|
+
|
180
|
+
specifications = yield
|
181
|
+
|
182
|
+
# Extract the first k/v
|
183
|
+
specification = specifications.first
|
184
|
+
|
185
|
+
# Based on the key for each row, take that content and postpend
|
186
|
+
# it.to_s to the ending of each value held in the hash element value
|
187
|
+
expanded_specification = combinatorialize(specification)
|
188
|
+
|
189
|
+
# Remove the expanded value from the specifications hash
|
190
|
+
specifications.delete(specification[0])
|
191
|
+
|
192
|
+
# Keep a record of aspects we have seen
|
193
|
+
@aspect_list.push specification[0]
|
194
|
+
@aspect_list.uniq!
|
195
|
+
|
196
|
+
# So we grepped the desired stems and stored them in matching_stems
|
197
|
+
# First we delete those stems (becasue we're going to further specify) them
|
198
|
+
matching_stems.each do |x|
|
199
|
+
@vector_list.delete x
|
200
|
+
expanded_specification.each do |u|
|
201
|
+
temp.push x+"_#{u}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Combine the original list with the freshly expanded list
|
206
|
+
@vector_list = (@vector_list + temp).sort
|
207
|
+
end
|
208
|
+
|
209
|
+
# Languages are not entirely rational, while something _ought_ exist
|
210
|
+
# by the rules of rational combination, some times they simply _don't_
|
211
|
+
# exist. That's what this method is for.
|
212
|
+
#
|
213
|
+
# +action+ :: +:remove+ or +:add+
|
214
|
+
# +id+ :: method name to remove
|
215
|
+
# _block_ :: used to add
|
216
|
+
def exception(action, id, &b)
|
217
|
+
if action == :remove
|
218
|
+
@vector_list.delete_if {|x| x =~ /#{id.to_s}/ }
|
219
|
+
elsif action == :add
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Method to take a hash key where the key is an _aspect_ and the value
|
224
|
+
# is an array of specifications valid for that _aspect_.
|
225
|
+
def combinatorialize(h)
|
226
|
+
results = []
|
227
|
+
h[1].each do |k|
|
228
|
+
results.push "#{k}_#{h[0]}"
|
229
|
+
end
|
230
|
+
results
|
231
|
+
end
|
232
|
+
|
233
|
+
# Method to allow "clusters" of simiar vectors to be identified (see:
|
234
|
+
# +match_upto+) based on the 0th string match. These clusters can be
|
235
|
+
# called by the method namd provided as the 2nd argument (0-index)
|
236
|
+
#
|
237
|
+
# This allows
|
238
|
+
# active_voice_indicative_mood_imperfect_tense_singular_number_third_p
|
239
|
+
# erson and other such to be 'clustered' as
|
240
|
+
# active_voice_indicative_mood_imperfect_tense. This means the actual
|
241
|
+
# method will probably be done in the "cluster" and some sort of
|
242
|
+
# secondary logic (method_missing) will do the final resolution
|
243
|
+
#
|
244
|
+
# Nevertheless, per the logic of this library, by defining all the
|
245
|
+
# atomic, we play nice and give respond_to? all the information it
|
246
|
+
# needs
|
247
|
+
#
|
248
|
+
# *Example:* <tt>cluster_on /active_voice.*third/, "as method", :active_thirds</tt>
|
249
|
+
#
|
250
|
+
#
|
251
|
+
# This means you want to collect several method names that match the Regexp and make them identifiable by a call to the
|
252
|
+
# method +active_thirds+
|
253
|
+
#
|
254
|
+
# Alternatively, you might want to use a String or Symbol (making use of match_upto).
|
255
|
+
#
|
256
|
+
# *Example:* <tt>cluster_on :tense, "as method", :tense_list</tt>
|
257
|
+
#
|
258
|
+
# +match+ :: The String or Regex match_upto will use for matching
|
259
|
+
# +junk+ :: Syntactic sugar, a string that makes the DSL look sensible
|
260
|
+
# +method_name+ :: The method in the anonymous module that returns the matched method names. See +create_module+
|
261
|
+
def cluster_on(match, junk, method_name)
|
262
|
+
|
263
|
+
clustered_matches =
|
264
|
+
if match.class == Regexp
|
265
|
+
@vector_list.grep match
|
266
|
+
elsif match.class.to_s =~ /^(String|Symbol)/
|
267
|
+
# Get the items that match_upto the specified clustering token
|
268
|
+
match_vector_upto_aspect(match.to_s)
|
269
|
+
else
|
270
|
+
# This shouldn't happen, we should get a Regexp or a String/Symbol
|
271
|
+
raise "Didn't fire for clustered match: #{match}"
|
272
|
+
end
|
273
|
+
|
274
|
+
unless clustered_matches.nil?
|
275
|
+
# No, this should not be done:
|
276
|
+
# ...and add them to the @vector_list.
|
277
|
+
# @vector_list += clustered_matches
|
278
|
+
# Clustered methods need to be defined, for real, somewhere. We
|
279
|
+
# should not claim to respond to them here, but rather let the
|
280
|
+
# framework using verbvector have the responsibility for
|
281
|
+
# implementation.
|
282
|
+
|
283
|
+
|
284
|
+
# Now, define a Proc that can correspond to arg[2]
|
285
|
+
@cluster_methods[method_name] = Proc.new do
|
286
|
+
clustered_matches
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
active_voice_imperative_mood_present_tense
|
2
|
+
active_voice_indicative_mood_present_tense
|
3
|
+
active_voice_indicative_mood_imperfect_tense
|
4
|
+
active_voice_indicative_mood_future_tense
|
5
|
+
active_voice_indicative_mood_perfect_tense
|
6
|
+
active_voice_indicative_mood_pastperfect_tense
|
7
|
+
active_voice_indicative_mood_futureperfect_tense
|
8
|
+
passive_voice_indicative_mood_present_tense
|
9
|
+
passive_voice_indicative_mood_imperfect_tense
|
10
|
+
passive_voice_indicative_mood_future_tense
|
11
|
+
passive_voice_indicative_mood_perfect_tense
|
12
|
+
passive_voice_indicative_mood_pastperfect_tense
|
13
|
+
passive_voice_indicative_mood_futureperfect_tense
|
14
|
+
active_voice_subjunctive_mood_present_tense
|
15
|
+
active_voice_subjunctive_mood_imperfect_tense
|
16
|
+
active_voice_subjunctive_mood_perfect_tense
|
17
|
+
active_voice_subjunctive_mood_pastperfect_tense
|
18
|
+
passive_voice_subjunctive_mood_present_tense
|
19
|
+
passive_voice_subjunctive_mood_imperfect_tense
|
20
|
+
passive_voice_subjunctive_mood_perfect_tense
|
21
|
+
passive_voice_subjunctive_mood_pastperfect_tense
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
require "verbvector"
|
5
|
+
|
6
|
+
class TestVerbVector < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@vv =
|
9
|
+
Linguistics::Verbs::Verbvector::VerbvectorGenerator.new do
|
10
|
+
language :Latin do
|
11
|
+
all_vectors :start_with do
|
12
|
+
{
|
13
|
+
:voice => %w(active passive),
|
14
|
+
:mood => %w(indicative subjunctive imperative)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
vectors_that /.*_indicative_mood/ do
|
18
|
+
{
|
19
|
+
:tense => %w(present imperfect future
|
20
|
+
perfect pastperfect futureperfect)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
vectors_that /.*_subjunctive_mood/ do
|
24
|
+
{
|
25
|
+
:tense => %w(present imperfect
|
26
|
+
perfect pastperfect)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
vectors_that /.*_imperative_mood/ do
|
30
|
+
{
|
31
|
+
:tense => %w(present)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
all_vectors :end_with do
|
35
|
+
{
|
36
|
+
:number => %w(singular plural),
|
37
|
+
:person => %w(first second third)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
exception :remove, :passive_voice_imperative_mood_present_tense
|
41
|
+
cluster_on :tense, "as method", :tense_list
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_basics
|
47
|
+
v = Linguistics::Verbs::Verbvector::VerbvectorGenerator.new do
|
48
|
+
end
|
49
|
+
|
50
|
+
assert_not_nil(v)
|
51
|
+
assert_equal(:Latin, @vv.language)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_vector_matcher
|
55
|
+
assert_equal 2, @vv.match_vector_upto_aspect("voice").length
|
56
|
+
assert_equal 5, @vv.match_vector_upto_aspect("mood").length
|
57
|
+
assert_equal 21, @vv.match_vector_upto_aspect("tense").length
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_tense_resolution
|
61
|
+
tense_list = Array.new(File.open(File.join(File.dirname(__FILE__), *%w[fixtures tense_list])).read.split /\s/)
|
62
|
+
tense_list.sort!
|
63
|
+
|
64
|
+
tc = Class.new
|
65
|
+
tc.extend @vv.method_extension_module
|
66
|
+
|
67
|
+
assert_equal tense_list, tc.tense_list
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_clustering_with_regex
|
71
|
+
t =
|
72
|
+
Linguistics::Verbs::Verbvector::VerbvectorGenerator.new do
|
73
|
+
language :Latin do
|
74
|
+
all_vectors :start_with do
|
75
|
+
{
|
76
|
+
:voice => %w(active passive),
|
77
|
+
:mood => %w(indicative subjunctive imperative)
|
78
|
+
}
|
79
|
+
end
|
80
|
+
vectors_that /.*_indicative_mood/ do
|
81
|
+
{
|
82
|
+
:tense => %w(present imperfect future
|
83
|
+
perfect pastperfect futureperfect)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
vectors_that /.*_subjunctive_mood/ do
|
87
|
+
{
|
88
|
+
:tense => %w(present imperfect
|
89
|
+
perfect pastperfect)
|
90
|
+
}
|
91
|
+
end
|
92
|
+
vectors_that /.*_imperative_mood/ do
|
93
|
+
{
|
94
|
+
:tense => %w(present)
|
95
|
+
}
|
96
|
+
end
|
97
|
+
all_vectors :end_with do
|
98
|
+
{
|
99
|
+
:number => %w(singular plural),
|
100
|
+
:person => %w(first second third)
|
101
|
+
}
|
102
|
+
end
|
103
|
+
exception :remove, :passive_voice_imperative_mood_present_tense
|
104
|
+
cluster_on /active_voice.*third/, "as method", :active_thirds
|
105
|
+
end
|
106
|
+
end
|
107
|
+
tc = Class.new
|
108
|
+
tc.extend t.method_extension_module
|
109
|
+
assert_respond_to(tc, :active_thirds)
|
110
|
+
assert_equal(22, tc.active_thirds.length)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_extension
|
114
|
+
tc = Class.new
|
115
|
+
tc.extend @vv.method_extension_module
|
116
|
+
assert_respond_to(tc, :latin_active_voice_indicative_mood_imperfect_tense_singular_number_third_person)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_clustering
|
120
|
+
assert_respond_to(@vv, :vectors_that)
|
121
|
+
assert_respond_to(@vv, :cluster_on)
|
122
|
+
|
123
|
+
tc = Class.new
|
124
|
+
tc.extend @vv.method_extension_module
|
125
|
+
|
126
|
+
# Make sure that each cluster method is /not/ defined. We want these to
|
127
|
+
# be defined "for real," not as a proxy.
|
128
|
+
|
129
|
+
cms=@vv.cluster_methods[:tense_list].call
|
130
|
+
cms.each do |m|
|
131
|
+
assert_true not(tc.respond_to? m.to_sym)
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
data/verbvector.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "verbvector/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "verbvector"
|
7
|
+
s.version = Linguistics::Verbs::Verbvector::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Steven G. Harms"]
|
10
|
+
s.email = ["github@sgharms.oib.com"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/verbvector"
|
12
|
+
s.summary = %q{Generates the verb tense "vectors" based on a DSL description syntax.}
|
13
|
+
s.description = %q{Use a DSL to describe the verb tense framework of a language and have it generate methods corresponding to each unique vector.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "verbvector"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: verbvector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Steven G. Harms
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-06-02 00:00:00 Z
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Use a DSL to describe the verb tense framework of a language and have it generate methods corresponding to each unique vector.
|
17
|
+
email:
|
18
|
+
- github@sgharms.oib.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- Gemfile
|
28
|
+
- README.textile
|
29
|
+
- Rakefile
|
30
|
+
- lib/verbvector.rb
|
31
|
+
- lib/verbvector/version.rb
|
32
|
+
- test/fixtures/tense_list
|
33
|
+
- test/test_generator.rb
|
34
|
+
- verbvector.gemspec
|
35
|
+
homepage: http://rubygems.org/gems/verbvector
|
36
|
+
licenses: []
|
37
|
+
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project: verbvector
|
58
|
+
rubygems_version: 1.8.5
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: Generates the verb tense "vectors" based on a DSL description syntax.
|
62
|
+
test_files:
|
63
|
+
- test/fixtures/tense_list
|
64
|
+
- test/test_generator.rb
|