linkparser 1.0.3
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/ChangeLog +526 -0
- data/LICENSE +27 -0
- data/README +88 -0
- data/Rakefile +315 -0
- data/Rakefile.local +60 -0
- data/ext/dictionary.c +269 -0
- data/ext/extconf.rb +53 -0
- data/ext/linkage.c +894 -0
- data/ext/linkparser.c +120 -0
- data/ext/linkparser.h +112 -0
- data/ext/parseoptions.c +1188 -0
- data/ext/sentence.c +536 -0
- data/lib/linkparser.rb +38 -0
- data/lib/linkparser/linkage.rb +248 -0
- data/lib/linkparser/sentence.rb +106 -0
- data/rake/dependencies.rb +76 -0
- data/rake/helpers.rb +395 -0
- data/rake/manual.rb +755 -0
- data/rake/packaging.rb +112 -0
- data/rake/publishing.rb +308 -0
- data/rake/rdoc.rb +47 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +602 -0
- data/rake/testing.rb +202 -0
- data/rake/verifytask.rb +64 -0
- data/spec/bugfixes_spec.rb +42 -0
- data/spec/linkparser/dictionary_spec.rb +90 -0
- data/spec/linkparser/linkage_spec.rb +434 -0
- data/spec/linkparser/parseoptions_spec.rb +78 -0
- data/spec/linkparser/sentence_spec.rb +117 -0
- data/spec/linkparser_spec.rb +30 -0
- metadata +219 -0
data/rake/testing.rb
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
#
|
2
|
+
# Rake tasklib for testing tasks
|
3
|
+
# $Id: testing.rb 80 2008-12-20 19:50:19Z deveiant $
|
4
|
+
#
|
5
|
+
# Authors:
|
6
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
7
|
+
#
|
8
|
+
|
9
|
+
unless defined?( COVERAGE_MINIMUM )
|
10
|
+
if ENV['COVVERAGE_MINIMUM']
|
11
|
+
COVERAGE_MINIMUM = Float( ENV['COVERAGE_MINIMUM'] )
|
12
|
+
else
|
13
|
+
COVERAGE_MINIMUM = 85.0
|
14
|
+
end
|
15
|
+
end
|
16
|
+
SPEC_FILES = [] unless defined?( SPEC_FILES )
|
17
|
+
TEST_FILES = [] unless defined?( TEST_FILES )
|
18
|
+
|
19
|
+
COMMON_SPEC_OPTS = ['-Du', '-b'] unless defined?( COMMON_SPEC_OPTS )
|
20
|
+
|
21
|
+
COVERAGE_TARGETDIR = BASEDIR + 'coverage' unless defined?( COVERAGE_TARGETDIR )
|
22
|
+
RCOV_EXCLUDES = 'spec,tests,/Library/Ruby,/var/lib,/usr/local/lib' unless
|
23
|
+
defined?( RCOV_EXCLUDES )
|
24
|
+
|
25
|
+
|
26
|
+
desc "Run all defined tests"
|
27
|
+
task :test do
|
28
|
+
unless SPEC_FILES.empty?
|
29
|
+
log "Running specs"
|
30
|
+
Rake::Task['spec:quiet'].invoke
|
31
|
+
end
|
32
|
+
|
33
|
+
unless TEST_FILES.empty?
|
34
|
+
log "Running unit tests"
|
35
|
+
Rake::Task[:unittests].invoke
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
### RSpec specifications
|
41
|
+
begin
|
42
|
+
gem 'rspec', '>= 1.1.3'
|
43
|
+
|
44
|
+
require 'spec'
|
45
|
+
require 'spec/rake/spectask'
|
46
|
+
|
47
|
+
### Task: spec
|
48
|
+
task :spec => 'spec:doc'
|
49
|
+
|
50
|
+
namespace :spec do
|
51
|
+
desc "Run rspec every time there's a change to one of the files"
|
52
|
+
task :autotest do
|
53
|
+
require 'autotest/rspec'
|
54
|
+
|
55
|
+
autotester = Autotest::Rspec.new
|
56
|
+
autotester.run
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "Generate regular color 'doc' spec output"
|
60
|
+
Spec::Rake::SpecTask.new( :doc ) do |task|
|
61
|
+
task.spec_files = SPEC_FILES
|
62
|
+
task.spec_opts = COMMON_SPEC_OPTS + ['-f', 's', '-c']
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "Generate spec output with profiling"
|
66
|
+
Spec::Rake::SpecTask.new( :profile ) do |task|
|
67
|
+
task.spec_files = SPEC_FILES
|
68
|
+
task.spec_opts = COMMON_SPEC_OPTS + ['-f', 'o']
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Generate quiet non-colored plain-text output"
|
72
|
+
Spec::Rake::SpecTask.new( :quiet ) do |task|
|
73
|
+
task.spec_files = SPEC_FILES
|
74
|
+
task.spec_opts = COMMON_SPEC_OPTS + ['-f', 'p']
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "Generate HTML output"
|
78
|
+
Spec::Rake::SpecTask.new( :html ) do |task|
|
79
|
+
task.spec_files = SPEC_FILES
|
80
|
+
task.spec_opts = COMMON_SPEC_OPTS + ['-f', 'h']
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
rescue LoadError => err
|
85
|
+
task :no_rspec do
|
86
|
+
$stderr.puts "Specification tasks not defined: %s" % [ err.message ]
|
87
|
+
end
|
88
|
+
|
89
|
+
task :spec => :no_rspec
|
90
|
+
namespace :spec do
|
91
|
+
task :autotest => :no_rspec
|
92
|
+
task :doc => :no_rspec
|
93
|
+
task :profile => :no_rspec
|
94
|
+
task :quiet => :no_rspec
|
95
|
+
task :html => :no_rspec
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
### Test::Unit tests
|
101
|
+
begin
|
102
|
+
require 'rake/testtask'
|
103
|
+
|
104
|
+
Rake::TestTask.new( :unittests ) do |task|
|
105
|
+
task.libs += [LIBDIR]
|
106
|
+
task.test_files = TEST_FILES
|
107
|
+
task.verbose = true
|
108
|
+
end
|
109
|
+
|
110
|
+
rescue LoadError => err
|
111
|
+
task :no_test do
|
112
|
+
$stderr.puts "Test tasks not defined: %s" % [ err.message ]
|
113
|
+
end
|
114
|
+
|
115
|
+
task :unittests => :no_rspec
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
### RCov (via RSpec) tasks
|
120
|
+
begin
|
121
|
+
gem 'rcov'
|
122
|
+
gem 'rspec', '>= 1.1.3'
|
123
|
+
|
124
|
+
require 'spec'
|
125
|
+
require 'rcov'
|
126
|
+
|
127
|
+
### Task: coverage (via RCov)
|
128
|
+
desc "Build test coverage reports"
|
129
|
+
unless SPEC_FILES.empty?
|
130
|
+
Spec::Rake::SpecTask.new( :coverage ) do |task|
|
131
|
+
task.spec_files = SPEC_FILES
|
132
|
+
task.libs += [LIBDIR]
|
133
|
+
task.spec_opts = ['-f', 'p', '-b']
|
134
|
+
task.rcov_opts = RCOV_OPTS
|
135
|
+
task.rcov = true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
# unless TEST_FILES.empty?
|
139
|
+
# require 'rcov/rcovtask'
|
140
|
+
|
141
|
+
# Rcov::RcovTask.new do |task|
|
142
|
+
# task.libs += [LIBDIR]
|
143
|
+
# task.test_files = TEST_FILES
|
144
|
+
# task.verbose = true
|
145
|
+
# task.rcov_opts = RCOV_OPTS
|
146
|
+
# end
|
147
|
+
# end
|
148
|
+
|
149
|
+
|
150
|
+
### Task: rcov
|
151
|
+
task :rcov => :coverage
|
152
|
+
|
153
|
+
### Other coverage tasks
|
154
|
+
namespace :coverage do
|
155
|
+
desc "Generate a detailed text coverage report"
|
156
|
+
Spec::Rake::SpecTask.new( :text ) do |task|
|
157
|
+
task.spec_files = SPEC_FILES
|
158
|
+
task.rcov_opts = RCOV_OPTS + ['--text-report']
|
159
|
+
task.rcov = true
|
160
|
+
end
|
161
|
+
|
162
|
+
desc "Show differences in coverage from last run"
|
163
|
+
Spec::Rake::SpecTask.new( :diff ) do |task|
|
164
|
+
task.spec_files = SPEC_FILES
|
165
|
+
task.spec_opts = ['-f', 'p', '-b']
|
166
|
+
task.rcov_opts = RCOV_OPTS - ['--save'] + ['--text-coverage-diff']
|
167
|
+
task.rcov = true
|
168
|
+
end
|
169
|
+
|
170
|
+
### Task: verify coverage
|
171
|
+
desc "Build coverage statistics"
|
172
|
+
VerifyTask.new( :verify => :rcov ) do |task|
|
173
|
+
task.threshold = COVERAGE_MINIMUM
|
174
|
+
end
|
175
|
+
|
176
|
+
desc "Run RCov in 'spec-only' mode to check coverage from specs"
|
177
|
+
Spec::Rake::SpecTask.new( :speconly ) do |task|
|
178
|
+
task.spec_files = SPEC_FILES
|
179
|
+
task.rcov_opts = ['--exclude', RCOV_EXCLUDES, '--text-report', '--save']
|
180
|
+
task.rcov = true
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
CLOBBER.include( COVERAGE_TARGETDIR )
|
185
|
+
|
186
|
+
rescue LoadError => err
|
187
|
+
task :no_rcov do
|
188
|
+
$stderr.puts "Coverage tasks not defined: RSpec+RCov tasklib not available: %s" %
|
189
|
+
[ err.message ]
|
190
|
+
end
|
191
|
+
|
192
|
+
task :coverage => :no_rcov
|
193
|
+
task :clobber_coverage
|
194
|
+
task :rcov => :no_rcov
|
195
|
+
namespace :coverage do
|
196
|
+
task :text => :no_rcov
|
197
|
+
task :diff => :no_rcov
|
198
|
+
end
|
199
|
+
task :verify => :no_rcov
|
200
|
+
end
|
201
|
+
|
202
|
+
|
data/rake/verifytask.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#####################################################################
|
2
|
+
### S U B V E R S I O N T A S K S A N D H E L P E R S
|
3
|
+
#####################################################################
|
4
|
+
|
5
|
+
require 'rake/tasklib'
|
6
|
+
|
7
|
+
#
|
8
|
+
# Work around the inexplicable behaviour of the original RDoc::VerifyTask, which
|
9
|
+
# errors if your coverage isn't *exactly* the threshold.
|
10
|
+
#
|
11
|
+
|
12
|
+
# A task that can verify that the RCov coverage doesn't
|
13
|
+
# drop below a certain threshold. It should be run after
|
14
|
+
# running Spec::Rake::SpecTask.
|
15
|
+
class VerifyTask < Rake::TaskLib
|
16
|
+
|
17
|
+
COVERAGE_PERCENTAGE_PATTERN =
|
18
|
+
%r{<tt class='coverage_code'>(\d+\.\d+)%</tt>}
|
19
|
+
|
20
|
+
# Name of the task. Defaults to :verify_rcov
|
21
|
+
attr_accessor :name
|
22
|
+
|
23
|
+
# Path to the index.html file generated by RCov, which
|
24
|
+
# is the file containing the total coverage.
|
25
|
+
# Defaults to 'coverage/index.html'
|
26
|
+
attr_accessor :index_html
|
27
|
+
|
28
|
+
# Whether or not to output details. Defaults to true.
|
29
|
+
attr_accessor :verbose
|
30
|
+
|
31
|
+
# The threshold value (in percent) for coverage. If the
|
32
|
+
# actual coverage is not equal to this value, the task will raise an
|
33
|
+
# exception.
|
34
|
+
attr_accessor :threshold
|
35
|
+
|
36
|
+
def initialize( name=:verify )
|
37
|
+
@name = name
|
38
|
+
@index_html = 'coverage/index.html'
|
39
|
+
@verbose = true
|
40
|
+
yield self if block_given?
|
41
|
+
raise "Threshold must be set" if @threshold.nil?
|
42
|
+
define
|
43
|
+
end
|
44
|
+
|
45
|
+
def define
|
46
|
+
desc "Verify that rcov coverage is at least #{threshold}%"
|
47
|
+
|
48
|
+
task @name do
|
49
|
+
total_coverage = nil
|
50
|
+
if match = File.read( index_html ).match( COVERAGE_PERCENTAGE_PATTERN )
|
51
|
+
total_coverage = Float( match[1] )
|
52
|
+
else
|
53
|
+
raise "Couldn't find the coverage percentage in #{index_html}"
|
54
|
+
end
|
55
|
+
|
56
|
+
puts "Coverage: #{total_coverage}% (threshold: #{threshold}%)" if verbose
|
57
|
+
if total_coverage < threshold
|
58
|
+
raise "Coverage must be at least #{threshold}% but was #{total_coverage}%"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# vim: set nosta noet ts=4 sw=4:
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
#
|
3
|
+
# Specification for various bugfixes to the LinkParser binding
|
4
|
+
# $Id: bugfixes_spec.rb 48 2008-12-19 18:30:33Z deveiant $
|
5
|
+
#
|
6
|
+
# See the LICENSE file in the distribution for information about copyright and licensing.
|
7
|
+
#
|
8
|
+
|
9
|
+
BEGIN {
|
10
|
+
require 'pathname'
|
11
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent
|
12
|
+
|
13
|
+
libdir = basedir + 'lib'
|
14
|
+
extdir = basedir + 'ext'
|
15
|
+
|
16
|
+
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
17
|
+
$LOAD_PATH.unshift( extdir.to_s ) unless $LOAD_PATH.include?( extdir.to_s )
|
18
|
+
}
|
19
|
+
|
20
|
+
require 'spec/runner'
|
21
|
+
require 'linkparser'
|
22
|
+
|
23
|
+
# @dict = LinkParser::Dictionary.new( :verbosity => 0 )
|
24
|
+
# s = LinkParser::Sentence.new('The cat runs.',@dict)
|
25
|
+
# puts s.linkages.first.verb # "cat.n" !?!?!
|
26
|
+
describe %{bugfix for #3: The first linkage for "The cat runs."} do
|
27
|
+
before( :each ) do
|
28
|
+
@dict = LinkParser::Dictionary.new( :verbosity => 0 )
|
29
|
+
@sentence = @dict.parse( "The cat runs." )
|
30
|
+
@linkage = @sentence.linkages.first
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
it "thinks cat is the subject" do
|
35
|
+
@linkage.subject.should == "cat"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "thinks runs is the verb" do
|
39
|
+
@linkage.verb.should == "runs"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
#
|
3
|
+
# Specification for the LinkParser::Dictionary class
|
4
|
+
# $Id: dictionary_spec.rb 48 2008-12-19 18:30:33Z deveiant $
|
5
|
+
#
|
6
|
+
# See the LICENSE file in the distribution for information about copyright and licensing.
|
7
|
+
#
|
8
|
+
|
9
|
+
BEGIN {
|
10
|
+
require 'pathname'
|
11
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
12
|
+
|
13
|
+
libdir = basedir + 'lib'
|
14
|
+
extdir = basedir + 'ext'
|
15
|
+
|
16
|
+
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
17
|
+
$LOAD_PATH.unshift( extdir.to_s ) unless $LOAD_PATH.include?( extdir.to_s )
|
18
|
+
}
|
19
|
+
|
20
|
+
require 'spec/runner'
|
21
|
+
require 'linkparser'
|
22
|
+
|
23
|
+
|
24
|
+
describe LinkParser::Dictionary do
|
25
|
+
it "can be instantiated using all default values" do
|
26
|
+
lambda { LinkParser::Dictionary.new }.should_not raise_error()
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can be instantiated with an options hash" do
|
30
|
+
LinkParser::Dictionary.new( :verbosity => 2 ).options[:verbosity].should == 2
|
31
|
+
end
|
32
|
+
|
33
|
+
it "raises an error when created with an bad number of arguments" do
|
34
|
+
lambda {
|
35
|
+
LinkParser::Dictionary.new( "foo", "bar", "baz" )
|
36
|
+
}.should raise_error(ArgumentError)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "can be instantiated with a language argument" do
|
40
|
+
lambda {LinkParser::Dictionary.new( 'en' )}.should_not raise_error()
|
41
|
+
end
|
42
|
+
|
43
|
+
it "can be instantiated with both a language and an options hash" do
|
44
|
+
LinkParser::Dictionary.new('en', :verbosity => 2).options[:verbosity].should == 2
|
45
|
+
end
|
46
|
+
|
47
|
+
it "raises an exception if created with unknown dictionaries" do
|
48
|
+
lambda {
|
49
|
+
LinkParser::Dictionary.new('foo', 'bar', 'baz', 'bim')
|
50
|
+
}.should raise_error( LinkParser::Error )
|
51
|
+
end
|
52
|
+
|
53
|
+
it "raises an exception if created with an unknown language" do
|
54
|
+
lambda {
|
55
|
+
LinkParser::Dictionary.new('zz')
|
56
|
+
}.should raise_error( LinkParser::Error )
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "An instance of LinkParser::Dictionary" do
|
62
|
+
|
63
|
+
TEST_SENTENCE = "The dog plays with the ball."
|
64
|
+
|
65
|
+
before( :each ) do
|
66
|
+
@dict = LinkParser::Dictionary.new(
|
67
|
+
:verbosity => 0,
|
68
|
+
:max_null_count => 18,
|
69
|
+
:echo_on => true
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
it "knows what the total cost of its linkages are" do
|
75
|
+
@dict.max_cost.should be_an_instance_of(Fixnum)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "can parse a sentence" do
|
79
|
+
@dict.parse( TEST_SENTENCE ).
|
80
|
+
should be_an_instance_of( LinkParser::Sentence )
|
81
|
+
end
|
82
|
+
|
83
|
+
it "passes on its options to the sentences it parses" do
|
84
|
+
sentence = @dict.parse( TEST_SENTENCE )
|
85
|
+
sentence.options.max_null_count.should == 18
|
86
|
+
sentence.options.verbosity.should == 0
|
87
|
+
sentence.options.echo_on?.should == true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,434 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
#
|
3
|
+
# Specification for the LinkParser::Linkage class
|
4
|
+
# $Id: linkage_spec.rb 48 2008-12-19 18:30:33Z deveiant $
|
5
|
+
#
|
6
|
+
# See the LICENSE file in the distribution for information about copyright and licensing.
|
7
|
+
#
|
8
|
+
|
9
|
+
BEGIN {
|
10
|
+
require 'pathname'
|
11
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
12
|
+
|
13
|
+
libdir = basedir + 'lib'
|
14
|
+
extdir = basedir + 'ext'
|
15
|
+
|
16
|
+
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
17
|
+
$LOAD_PATH.unshift( extdir.to_s ) unless $LOAD_PATH.include?( extdir.to_s )
|
18
|
+
}
|
19
|
+
|
20
|
+
require 'spec/runner'
|
21
|
+
require 'linkparser'
|
22
|
+
|
23
|
+
|
24
|
+
describe LinkParser::Linkage do
|
25
|
+
|
26
|
+
before( :all ) do
|
27
|
+
@dict = LinkParser::Dictionary.new( :verbosity => 0 )
|
28
|
+
end
|
29
|
+
|
30
|
+
before( :each ) do
|
31
|
+
@sentence = @dict.parse( "The flag was wet." )
|
32
|
+
@linkage = @sentence.linkages.first
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# +-------------Xp-------------+
|
37
|
+
# +-----Wd-----+ |
|
38
|
+
# | +--Ds-+--Ss-+--Pa-+ |
|
39
|
+
# | | | | | |
|
40
|
+
# LEFT-WALL the flag.n was.v wet.a .
|
41
|
+
it "can build a diagram string for a sentence" do
|
42
|
+
@linkage.diagram.should =~ /LEFT-WALL/
|
43
|
+
@linkage.diagram.should =~ /the/
|
44
|
+
@linkage.diagram.should =~ /flag\.n/
|
45
|
+
@linkage.diagram.should =~ /was\.v/
|
46
|
+
@linkage.diagram.should =~ /wet\.a/
|
47
|
+
|
48
|
+
@linkage.diagram.should =~ /-Xp-/
|
49
|
+
@linkage.diagram.should =~ /-Wd-/
|
50
|
+
@linkage.diagram.should =~ /-Ds-/
|
51
|
+
@linkage.diagram.should =~ /-Ss-/
|
52
|
+
@linkage.diagram.should =~ /-Pa-/
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# LEFT-WALL Xp <---Xp----> Xp .
|
57
|
+
# (m) LEFT-WALL Wd <---Wd----> Wd flag.n
|
58
|
+
# (m) the D <---Ds----> Ds flag.n
|
59
|
+
# (m) flag.n Ss <---Ss----> Ss was.v
|
60
|
+
# (m) was.v Pa <---Pa----> Pa wet.a
|
61
|
+
# . RW <---RW----> RW RIGHT-WALL
|
62
|
+
it "can build a 'links and domains' diagram" do
|
63
|
+
@linkage.links_and_domains.should =~ /LEFT-WALL/
|
64
|
+
@linkage.links_and_domains.should =~ /the/
|
65
|
+
@linkage.links_and_domains.should =~ /flag\.n/
|
66
|
+
@linkage.links_and_domains.should =~ /was\.v/
|
67
|
+
@linkage.links_and_domains.should =~ /wet\.a/
|
68
|
+
|
69
|
+
@linkage.links_and_domains.should =~ /-Xp-/
|
70
|
+
@linkage.links_and_domains.should =~ /-Wd-/
|
71
|
+
@linkage.links_and_domains.should =~ /-Ds-/
|
72
|
+
@linkage.links_and_domains.should =~ /-Ss-/
|
73
|
+
@linkage.links_and_domains.should =~ /-Pa-/
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
it "knows how many words are in the sentence" do
|
78
|
+
# LEFT-WALL + words + '.' + RIGHT-WALL = 7
|
79
|
+
@linkage.num_words.should == 7
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
it "can return a list of the tokenized words" do
|
84
|
+
@linkage.words.should include("LEFT-WALL")
|
85
|
+
@linkage.words.should include("the")
|
86
|
+
@linkage.words.should include("flag.n")
|
87
|
+
@linkage.words.should include("was.v")
|
88
|
+
@linkage.words.should include("wet.a")
|
89
|
+
@linkage.words.should include(".")
|
90
|
+
@linkage.words.should include("RIGHT-WALL")
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
it "knows how many links are in the sentence" do
|
95
|
+
@linkage.num_links.should == 6
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
it "can return the left word for any of its links" do
|
100
|
+
# LEFT-WALL Xp <---Xp----> Xp .
|
101
|
+
@linkage.link_lword( 0 ).should == @linkage.words.index('LEFT-WALL')
|
102
|
+
|
103
|
+
# (m) LEFT-WALL Wd <---Wd----> Wd flag.n
|
104
|
+
@linkage.link_lword( 1 ).should == @linkage.words.index('LEFT-WALL')
|
105
|
+
|
106
|
+
# (m) the D <---Ds----> Ds flag.n
|
107
|
+
@linkage.link_lword( 2 ).should == @linkage.words.index('the')
|
108
|
+
|
109
|
+
# (m) flag.n Ss <---Ss----> Ss was.v
|
110
|
+
@linkage.link_lword( 3 ).should == @linkage.words.index('flag.n')
|
111
|
+
|
112
|
+
# (m) was.v Pa <---Pa----> Pa wet.a
|
113
|
+
@linkage.link_lword( 4 ).should == @linkage.words.index('was.v')
|
114
|
+
|
115
|
+
# . RW <---RW----> RW RIGHT-WALL
|
116
|
+
@linkage.link_lword( 5 ).should == @linkage.words.index('.')
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
it "can return the right word for any of its links" do
|
121
|
+
# LEFT-WALL Xp <---Xp----> Xp .
|
122
|
+
@linkage.link_rword( 0 ).should == @linkage.words.index('.')
|
123
|
+
|
124
|
+
# (m) LEFT-WALL Wd <---Wd----> Wd flag.n
|
125
|
+
@linkage.link_rword( 1 ).should == @linkage.words.index('flag.n')
|
126
|
+
|
127
|
+
# (m) the D <---Ds----> Ds flag.n
|
128
|
+
@linkage.link_rword( 2 ).should == @linkage.words.index('flag.n')
|
129
|
+
|
130
|
+
# (m) flag.n Ss <---Ss----> Ss was.v
|
131
|
+
@linkage.link_rword( 3 ).should == @linkage.words.index('was.v')
|
132
|
+
|
133
|
+
# (m) was.v Pa <---Pa----> Pa wet.a
|
134
|
+
@linkage.link_rword( 4 ).should == @linkage.words.index('wet.a')
|
135
|
+
|
136
|
+
# . RW <---RW----> RW RIGHT-WALL
|
137
|
+
@linkage.link_rword( 5 ).should == @linkage.words.index('RIGHT-WALL')
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
it "can return the length of any of its links" do
|
143
|
+
@linkage.link_length( 0 ).should == 5
|
144
|
+
@linkage.link_length( 1 ).should == 2
|
145
|
+
@linkage.link_length( 2 ).should == 1
|
146
|
+
@linkage.link_length( 3 ).should == 1
|
147
|
+
@linkage.link_length( 4 ).should == 1
|
148
|
+
@linkage.link_length( 5 ).should == 1
|
149
|
+
|
150
|
+
# Out-of-bounds just returns -1
|
151
|
+
@linkage.link_length( 7 ).should == -1
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
it "can return labels for any of its links" do
|
156
|
+
@linkage.link_label( 0 ).should == "Xp"
|
157
|
+
@linkage.link_label( 1 ).should == "Wd"
|
158
|
+
@linkage.link_label( 2 ).should == "Ds"
|
159
|
+
@linkage.link_label( 3 ).should == "Ss"
|
160
|
+
@linkage.link_label( 4 ).should == "Pa"
|
161
|
+
@linkage.link_label( 5 ).should == "RW"
|
162
|
+
|
163
|
+
@linkage.link_label( 7 ).should be_nil
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
it "can return left labels for any of its links" do
|
168
|
+
@linkage.link_llabel( 0 ).should == "Xp"
|
169
|
+
@linkage.link_llabel( 1 ).should == "Wd"
|
170
|
+
@linkage.link_llabel( 2 ).should == "D"
|
171
|
+
@linkage.link_llabel( 3 ).should == "Ss"
|
172
|
+
@linkage.link_llabel( 4 ).should == "Pa"
|
173
|
+
@linkage.link_llabel( 5 ).should == "RW"
|
174
|
+
|
175
|
+
@linkage.link_llabel( 7 ).should be_nil
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
it "can return labels for any of its links" do
|
180
|
+
@linkage.link_rlabel( 0 ).should == "Xp"
|
181
|
+
@linkage.link_rlabel( 1 ).should == "Wd"
|
182
|
+
@linkage.link_rlabel( 2 ).should == "Ds"
|
183
|
+
@linkage.link_rlabel( 3 ).should == "Ss"
|
184
|
+
@linkage.link_rlabel( 4 ).should == "Pa"
|
185
|
+
@linkage.link_rlabel( 5 ).should == "RW"
|
186
|
+
|
187
|
+
@linkage.link_rlabel( 7 ).should be_nil
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
it "can return the number of domains for any link" do
|
192
|
+
@linkage.link_num_domains( 0 ).should == 0
|
193
|
+
1.upto(4) do |i|
|
194
|
+
@linkage.link_num_domains( i ).should == 1
|
195
|
+
end
|
196
|
+
@linkage.link_num_domains( 5 ).should == 0
|
197
|
+
|
198
|
+
@linkage.link_num_domains( 112 ).should == -1
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
it "can return the names of the domains of any of its links" do
|
203
|
+
@linkage.link_domain_names( 0 ).should be_an_instance_of( Array )
|
204
|
+
@linkage.link_domain_names( 0 ).should be_empty
|
205
|
+
|
206
|
+
1.upto(4) do |i|
|
207
|
+
@linkage.link_domain_names( i ).should be_an_instance_of( Array )
|
208
|
+
@linkage.link_domain_names( i ).should == ["m"]
|
209
|
+
end
|
210
|
+
|
211
|
+
@linkage.link_domain_names( 5 ).should be_an_instance_of( Array )
|
212
|
+
@linkage.link_domain_names( 5 ).should be_empty
|
213
|
+
|
214
|
+
@linkage.link_domain_names( 12 ).should be_an_instance_of( Array )
|
215
|
+
@linkage.link_domain_names( 12 ).should be_empty
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
it "can report on the various cost metrics of the parse" do
|
220
|
+
@linkage.unused_word_cost.should be_an_instance_of( Fixnum )
|
221
|
+
@linkage.disjunct_cost.should be_an_instance_of( Fixnum )
|
222
|
+
@linkage.and_cost.should be_an_instance_of( Fixnum )
|
223
|
+
@linkage.link_cost.should be_an_instance_of( Fixnum )
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
### :FIXME: I don't know what these do/mean yet, so for now just test to
|
228
|
+
### make sure they're implemented. They should really be tested with
|
229
|
+
### sentences that have predictable results.
|
230
|
+
it "implements Link Grammar predicate methods" do
|
231
|
+
@linkage.should respond_to( :canonical? )
|
232
|
+
@linkage.should respond_to( :improper? )
|
233
|
+
@linkage.should respond_to( :has_inconsistent_domains? )
|
234
|
+
@linkage.should respond_to( :violation_name )
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
# LEFT-WALL Xp <---Xp----> Xp .
|
239
|
+
# (m) LEFT-WALL Wd <---Wd----> Wd flag.n
|
240
|
+
# (m) the D <---Ds----> Ds flag.n
|
241
|
+
# (m) flag.n Ss <---Ss----> Ss was.v
|
242
|
+
# (m) was.v Pa <---Pa----> Pa wet.a
|
243
|
+
# . RW <---RW----> RW RIGHT-WALL
|
244
|
+
it "contains link structs describing the linkage" do
|
245
|
+
@linkage.should have(6).links
|
246
|
+
@linkage.links.should be_an_instance_of( Array )
|
247
|
+
|
248
|
+
@linkage.links.each do |link|
|
249
|
+
link.should be_a_kind_of( Struct )
|
250
|
+
end
|
251
|
+
|
252
|
+
@linkage.links.first.lword.should == 'LEFT-WALL'
|
253
|
+
@linkage.links.first.label.should == 'Xp'
|
254
|
+
@linkage.links.last.rword.should == 'RIGHT-WALL'
|
255
|
+
@linkage.links.last.label.should == 'RW'
|
256
|
+
@linkage.links[3].lword.should == 'flag.n'
|
257
|
+
@linkage.links[3].rword.should == 'was.v'
|
258
|
+
@linkage.links[3].label.should == 'Ss'
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
it "knows what word is the verb in the sentence" do
|
263
|
+
@linkage.verb.should == "was"
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
it "knows when the sentence doesn't have a direct object" do
|
268
|
+
@linkage.object.should be_nil()
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
MODE1_C_TREE_STRING = "(S (NP The flag)\n (VP was\n (ADJP wet))\n .)\n"
|
273
|
+
MODE2_C_TREE_STRING = "[S [NP The flag NP] [VP was [ADJP wet ADJP] VP] . S] \n"
|
274
|
+
MODE3_C_TREE_STRING = "(S (NP The flag) (VP was (ADJP wet)) .)\n"
|
275
|
+
|
276
|
+
it "returns an indented sexps for the constituent tree string by default (mode 1)" do
|
277
|
+
@linkage.constituent_tree_string.should == MODE1_C_TREE_STRING
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
it "returns indented sexps for the constituent tree string if fetched with explicit mode '1'" do
|
282
|
+
@linkage.constituent_tree_string( 1 ).should == MODE1_C_TREE_STRING
|
283
|
+
end
|
284
|
+
|
285
|
+
it "returns bracketed constituents if constituent tree string is fetched in mode 2" do
|
286
|
+
@linkage.constituent_tree_string( 2 ).should == MODE2_C_TREE_STRING
|
287
|
+
end
|
288
|
+
|
289
|
+
it "returns unindented sexps for the constituent tree string if constituent tree string " +
|
290
|
+
"is fetched in mode 3" do
|
291
|
+
@linkage.constituent_tree_string( 3 ).should == MODE3_C_TREE_STRING
|
292
|
+
end
|
293
|
+
|
294
|
+
it "raises an exception for any numeric constituent tree string mode greater than 3" do
|
295
|
+
lambda {
|
296
|
+
@linkage.constituent_tree_string( 4 )
|
297
|
+
}.should raise_error( ArgumentError, /illegal mode 4/i )
|
298
|
+
end
|
299
|
+
|
300
|
+
it "raises an exception for any numeric constituent tree string mode less than 1" do
|
301
|
+
lambda {
|
302
|
+
@linkage.constituent_tree_string( 0 )
|
303
|
+
}.should raise_error( ArgumentError, /illegal mode 0/i )
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
it "raises an exception when a non-numeric constituent tree string mode is given" do
|
308
|
+
lambda {
|
309
|
+
@linkage.constituent_tree_string( 'glarg' )
|
310
|
+
}.should raise_error( TypeError )
|
311
|
+
end
|
312
|
+
|
313
|
+
it "returns an Array of CTree structs for its constituent tree" do
|
314
|
+
rval = @linkage.constituent_tree
|
315
|
+
|
316
|
+
rval.should be_an_instance_of( Array )
|
317
|
+
rval.should have(1).members
|
318
|
+
rval.first.should be_a_kind_of( Struct )
|
319
|
+
rval.first.label.should == 'S'
|
320
|
+
rval.first.children.should have(3).members
|
321
|
+
rval.first.children.collect {|n| n.label }.should include( 'NP', 'VP', '.' )
|
322
|
+
end
|
323
|
+
|
324
|
+
it "returns 0 as the number of the current sublinkage since it has no conjunctions" do
|
325
|
+
@linkage.current_sublinkage.should == 0
|
326
|
+
end
|
327
|
+
|
328
|
+
|
329
|
+
it "returns an informational string when inspected" do
|
330
|
+
@linkage.inspect.should =~ /Linkage:0x[[:xdigit:]]+: sublinkage 0: \[\d+ links\]/
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
describe "from a simple sentence with a direct object" do
|
335
|
+
before( :each ) do
|
336
|
+
@sentence = @dict.parse( "The dog ran home." )
|
337
|
+
@linkage = @sentence.linkages.first
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
it "doesn't have any sublinkages" do
|
342
|
+
@linkage.num_sublinkages.should == 1
|
343
|
+
end
|
344
|
+
|
345
|
+
it "doesn't change after computing its union" do
|
346
|
+
lambda {
|
347
|
+
@linkage.compute_union
|
348
|
+
}.should_not change( @linkage, :num_sublinkages )
|
349
|
+
end
|
350
|
+
|
351
|
+
|
352
|
+
it "knows what word is the object in the sentence" do
|
353
|
+
@linkage.object.should == 'home'
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
it "knows that it doesn't have any conjunctions" do
|
360
|
+
@linkage.should_not have_conjunction()
|
361
|
+
end
|
362
|
+
|
363
|
+
|
364
|
+
describe "from a sentence with a conjunction" do
|
365
|
+
before( :each ) do
|
366
|
+
@sentence =
|
367
|
+
@dict.parse( "The ball rolled down the hill and bumped the curb." )
|
368
|
+
@linkage = @sentence.linkages.first
|
369
|
+
end
|
370
|
+
|
371
|
+
|
372
|
+
it "knows that it has a conjunction" do
|
373
|
+
@linkage.should have_conjunction()
|
374
|
+
end
|
375
|
+
|
376
|
+
it "has two sublinkages" do
|
377
|
+
@linkage.num_sublinkages.should == 2
|
378
|
+
end
|
379
|
+
|
380
|
+
|
381
|
+
it "adds a sublinkage after computing its union" do
|
382
|
+
lambda {
|
383
|
+
@linkage.compute_union
|
384
|
+
}.should change( @linkage, :num_sublinkages ).from(2).to(3)
|
385
|
+
end
|
386
|
+
|
387
|
+
|
388
|
+
it "knows what word is the verb in the current sublinkage" do
|
389
|
+
@linkage.verb.should == 'rolled'
|
390
|
+
@linkage.current_sublinkage = 1
|
391
|
+
@linkage.verb.should == 'bumped'
|
392
|
+
end
|
393
|
+
|
394
|
+
|
395
|
+
it "knows what word is the object in the current sublinkage" do
|
396
|
+
@linkage.object.should == 'hill'
|
397
|
+
@linkage.current_sublinkage = 1
|
398
|
+
@linkage.object.should == 'curb'
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
|
403
|
+
|
404
|
+
it "should know that it's not an imperative sentence" do
|
405
|
+
@linkage.imperative?.should be_false()
|
406
|
+
end
|
407
|
+
|
408
|
+
|
409
|
+
describe "from an imperative sentence" do
|
410
|
+
before( :each ) do
|
411
|
+
@sentence = @dict.parse( "Go to the store!" )
|
412
|
+
@linkage = @sentence.linkages.first
|
413
|
+
end
|
414
|
+
|
415
|
+
|
416
|
+
it "knows that it's an imperative sentence" do
|
417
|
+
@linkage.imperative?.should be_true()
|
418
|
+
end
|
419
|
+
|
420
|
+
|
421
|
+
end
|
422
|
+
|
423
|
+
|
424
|
+
describe "bugfixes" do
|
425
|
+
|
426
|
+
it "also strips off the '.p' from the subject and object when they are plural" do
|
427
|
+
sent = @dict.parse( 'People like goats.' )
|
428
|
+
sent.subject.should_not =~ /people\.p/i
|
429
|
+
sent.object.should_not =~ /goats\.p/i
|
430
|
+
end
|
431
|
+
|
432
|
+
end
|
433
|
+
|
434
|
+
end
|