swissparser 0.10.0 → 0.11.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.
- data/CHANGELOG.rdoc +14 -1
- data/Rakefile +1 -1
- data/features/basic_parsing.feature +30 -0
- data/features/parser_extension.feature +34 -4
- data/features/parsing_context.feature +48 -0
- data/features/polite.feature +16 -0
- data/features/step_definitions/core.rb +71 -0
- data/features/step_definitions/definitions.rb +68 -0
- data/features/step_definitions/extra.rb +47 -0
- data/lib/swissparser.rb +37 -100
- data/lib/swissparser/parsing_context.rb +60 -0
- data/lib/swissparser/parsing_rules.rb +39 -0
- metadata +10 -4
- data/features/step_definitions/steps.rb +0 -103
- data/features/user_friendly.feature +0 -29
data/CHANGELOG.rdoc
CHANGED
@@ -1,4 +1,17 @@
|
|
1
|
-
== 0.
|
1
|
+
== 0.11.0 / 2009-11-16
|
2
|
+
|
3
|
+
* 1 new feature:
|
4
|
+
- parsing rules can call +skip_entry!+ to skip the current entry, shortcutting other rules evaluation and entry finalizing.
|
5
|
+
|
6
|
+
* 1 improvement:
|
7
|
+
- parsers raise an error when the last separator is missing
|
8
|
+
|
9
|
+
* Code quality:
|
10
|
+
- Split swissparser file in several files
|
11
|
+
- Simplified parse method by extracting code
|
12
|
+
|
13
|
+
|
14
|
+
== 0.10.0 / 2009-11-15
|
2
15
|
|
3
16
|
* 1 new feature:
|
4
17
|
- SwissParser can now parse an input string or whatever input responding to the +each_line+ method
|
data/Rakefile
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
Feature: Basic Parsing
|
2
|
+
I can parse from different sources
|
3
|
+
|
4
|
+
Background:
|
5
|
+
Given input data
|
6
|
+
"""
|
7
|
+
XX a1
|
8
|
+
YY b1
|
9
|
+
c1
|
10
|
+
//
|
11
|
+
XX a2
|
12
|
+
YY b2
|
13
|
+
c2
|
14
|
+
//
|
15
|
+
"""
|
16
|
+
|
17
|
+
Scenario: Extension without redefinition
|
18
|
+
Given a simple parser
|
19
|
+
When I run the simple parser on data
|
20
|
+
Then the result should be "[{'XX'=>'a1','YY'=>'b1'},{'XX'=>'a2','YY'=>'b2'}]"
|
21
|
+
|
22
|
+
Scenario: Parsing from file
|
23
|
+
Given a simple parser
|
24
|
+
When I run it on file "input.txt"
|
25
|
+
Then File.open should be called with "input.txt"
|
26
|
+
|
27
|
+
Scenario: Parsing from URI
|
28
|
+
Given a simple parser
|
29
|
+
When I run it on a remote file "http://www.example.com/input.txt"
|
30
|
+
Then OpenUri.open should be called with "http://www.example.com/input.txt"
|
@@ -25,7 +25,8 @@ Feature: Parser Extension
|
|
25
25
|
When I extend it
|
26
26
|
And I replace with("XX") to return always 'foo'
|
27
27
|
And I replace with("YY") to do nothing
|
28
|
-
|
28
|
+
And I run the extended parser on data
|
29
|
+
Then the result should be "[{ 'XX' => 'foo'}, { 'XX' => 'foo'}]"
|
29
30
|
|
30
31
|
Scenario: Text after replacing
|
31
32
|
Given a simple parser
|
@@ -33,9 +34,10 @@ Feature: Parser Extension
|
|
33
34
|
And I replace with("XX") to do nothing
|
34
35
|
And I replace with("YY") to return always 'bar'
|
35
36
|
And I replace with_text_after("YY") to return always 'foo'
|
36
|
-
|
37
|
+
And I run the extended parser on data
|
38
|
+
Then the result should be "[{ 'YY' => 'bar', 'txt-YY' => 'foo'}, { 'YY' => 'bar', 'txt-YY' => 'foo'}]"
|
37
39
|
|
38
|
-
Scenario:
|
40
|
+
Scenario: Separator replacement
|
39
41
|
Given a simple parser
|
40
42
|
And input data
|
41
43
|
"""
|
@@ -50,4 +52,32 @@ Feature: Parser Extension
|
|
50
52
|
"""
|
51
53
|
When I extend it
|
52
54
|
And I set the separator to '%'
|
53
|
-
|
55
|
+
And I run the extended parser on data
|
56
|
+
Then the result should contain '2' entries
|
57
|
+
|
58
|
+
Scenario: Using custom entries objects
|
59
|
+
Given a simple parser
|
60
|
+
When I extend it
|
61
|
+
And I replace with("XX") to do nothing
|
62
|
+
And I replace with("YY") to do nothing
|
63
|
+
And I return "foo" in new entry
|
64
|
+
And I run the extended parser on data
|
65
|
+
Then the result should be "['foo','foo']"
|
66
|
+
|
67
|
+
Scenario: Changing the container
|
68
|
+
Given a simple parser
|
69
|
+
When I extend it
|
70
|
+
And I replace the container with a counter
|
71
|
+
And I run the extended parser on data
|
72
|
+
Then the result should be "2"
|
73
|
+
|
74
|
+
Scenario: Changing the entry finalization
|
75
|
+
Given a simple parser
|
76
|
+
When I extend it
|
77
|
+
And entry finalize always returns "foo"
|
78
|
+
And I run the extended parser on data
|
79
|
+
Then the result should be "['foo', 'foo']"
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
Feature: Sharing context
|
2
|
+
During parsing, rules share a context object.
|
3
|
+
|
4
|
+
Background:
|
5
|
+
Given input data
|
6
|
+
"""
|
7
|
+
XX a1
|
8
|
+
YY b1
|
9
|
+
c1
|
10
|
+
//
|
11
|
+
XX a1
|
12
|
+
YY b2
|
13
|
+
c2
|
14
|
+
//
|
15
|
+
"""
|
16
|
+
|
17
|
+
Scenario: Helper Method
|
18
|
+
Given a simple parser
|
19
|
+
When I extend it
|
20
|
+
And I define 'foo' helper
|
21
|
+
And I call 'foo' helper in after action
|
22
|
+
And I run the extended parser on data
|
23
|
+
Then the result should be "'foo'"
|
24
|
+
|
25
|
+
Scenario: Parsing Parameters
|
26
|
+
Given a simple parser
|
27
|
+
When I extend it
|
28
|
+
And I return param "foo" in after action
|
29
|
+
And I run the extended parser on data with param "foo" = "bar"
|
30
|
+
Then the result should be "'bar'"
|
31
|
+
|
32
|
+
Scenario: Instance variables
|
33
|
+
Given a simple parser
|
34
|
+
When I extend it
|
35
|
+
And the before action sets @foo="bar"
|
36
|
+
And the after action returns @foo
|
37
|
+
And I run the extended parser on data
|
38
|
+
Then the result should be "'bar'"
|
39
|
+
|
40
|
+
Scenario: Skipping entries
|
41
|
+
Given a simple parser
|
42
|
+
When I extend it
|
43
|
+
And set with("XX") to skip the entry
|
44
|
+
And I run the extended parser on data
|
45
|
+
Then the result should contain '0' entries
|
46
|
+
|
47
|
+
|
48
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Feature: Politeness
|
2
|
+
SwissParser is polite and reporst errors
|
3
|
+
|
4
|
+
Scenario: Missing
|
5
|
+
Given input data
|
6
|
+
"""
|
7
|
+
XX a1
|
8
|
+
YY b1
|
9
|
+
c1
|
10
|
+
//
|
11
|
+
XX a2
|
12
|
+
YY b2
|
13
|
+
c2
|
14
|
+
"""
|
15
|
+
And a simple parser
|
16
|
+
Then the simple parser should raise an error when parsing data
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'lib/swissparser'
|
2
|
+
require 'spec/expectations'
|
3
|
+
require 'spec/mocks'
|
4
|
+
|
5
|
+
|
6
|
+
Given /^a simple parser$/ do
|
7
|
+
@simple_parser = Swiss::Parser.define do
|
8
|
+
rules do
|
9
|
+
with("XX") {|c,e| e["XX"] = c}
|
10
|
+
with("YY") {|c,e| e["YY"] = c}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Given /^input data$/ do |string|
|
16
|
+
@data = string
|
17
|
+
end
|
18
|
+
|
19
|
+
When /^I extend it$/ do
|
20
|
+
@ext_parser = @simple_parser.extend {}
|
21
|
+
end
|
22
|
+
|
23
|
+
When /^I run the simple parser on data$/ do
|
24
|
+
@result = @simple_parser.parse(@data)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
When /^I run the extended parser on data$/ do
|
29
|
+
@result = @ext_parser.parse(@data)
|
30
|
+
end
|
31
|
+
|
32
|
+
When /^I run the extended parser on data with param "([^\"]*)" = "([^\"]*)"$/ do |key, val|
|
33
|
+
@result = @ext_parser.parse(@data, key => val)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
When /^I run it on file "([^\"]*)"$/ do |filename|
|
38
|
+
File.stub!(:open).and_return(@data)
|
39
|
+
@result = @simple_parser.parse_file( filename )
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
When /^I run it on a remote file "([^\"]*)"$/ do |arg1|
|
44
|
+
OpenURI.stub!(:open).and_return(@data)
|
45
|
+
end
|
46
|
+
|
47
|
+
Then /^the extended parser should parse it as the original one$/ do
|
48
|
+
@simple_parser.parse( @data ).should == @ext_parser.parse( @data )
|
49
|
+
end
|
50
|
+
|
51
|
+
Then /^the result should be "([^\"]*)"$/ do |ruby_exp|
|
52
|
+
result = eval(ruby_exp)
|
53
|
+
@result.should == result
|
54
|
+
end
|
55
|
+
|
56
|
+
Then /^the result should contain '([^\']*)' entries$/ do |n|
|
57
|
+
@result.size.should == n.to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
Then /^File\.open should be called with "([^\"]*)"$/ do |filename|
|
62
|
+
File.should_receive(:open).with(filename,'w')
|
63
|
+
end
|
64
|
+
|
65
|
+
Then /^OpenUri\.open should be called with "([^\"]*)"$/ do |filename|
|
66
|
+
OpenURI.should_receive(:open).with(filename)
|
67
|
+
end
|
68
|
+
|
69
|
+
Then /^the simple parser should raise an error when parsing data$/ do
|
70
|
+
lambda{@simple_parser.parse(@data)}.should raise_error
|
71
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'lib/swissparser'
|
2
|
+
require 'spec/expectations'
|
3
|
+
|
4
|
+
When /^I replace with\("([^\"]*)"\) to return always '([^\']*)'$/ do |key,out|
|
5
|
+
@ext_parser = @ext_parser.extend do
|
6
|
+
rules do
|
7
|
+
with( key ) {|c,e| e[key] = out }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^I replace with\("([^\"]*)"\) to do nothing$/ do |key|
|
13
|
+
@ext_parser = @ext_parser.extend do
|
14
|
+
rules do
|
15
|
+
with( key ) {|c,e| }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
When /^I replace with_text_after\("([^\"]*)"\) to return always '([^\']*)'$/ do |key,out|
|
21
|
+
text_key = "txt-#{key}"
|
22
|
+
@ext_parser = @ext_parser.extend do
|
23
|
+
rules do
|
24
|
+
with_text_after( key ) {|c,e| e[text_key] = out }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
When /^I set the separator to '([^\']*)'$/ do |sep|
|
30
|
+
@ext_parser = @ext_parser.extend do
|
31
|
+
rules do
|
32
|
+
set_separator( sep )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
When /^I return "([^\"]*)" in new entry$/ do |value|
|
38
|
+
@ext_parser = @ext_parser.extend do
|
39
|
+
new_entry { value }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
When /^I replace the container with a counter$/ do
|
44
|
+
class Counter
|
45
|
+
def initialize
|
46
|
+
@n = 0
|
47
|
+
end
|
48
|
+
def <<(i)
|
49
|
+
@n += 1
|
50
|
+
end
|
51
|
+
def count
|
52
|
+
@n
|
53
|
+
end
|
54
|
+
end
|
55
|
+
@ext_parser = @ext_parser.extend do
|
56
|
+
before { Counter.new }
|
57
|
+
after {|c| c.count }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
When /^entry finalize always returns "([^\"]*)"$/ do |val|
|
62
|
+
@ext_parser = @ext_parser.extend do
|
63
|
+
finish_entry {|e,c| c << val }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'lib/swissparser'
|
2
|
+
require 'spec/expectations'
|
3
|
+
|
4
|
+
When /^I define '([^\']*)' helper$/ do |name|
|
5
|
+
@ext_parser = @ext_parser.extend do
|
6
|
+
helper(name.to_sym) do
|
7
|
+
name
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^I call '([^\']*)' helper in after action$/ do |name|
|
13
|
+
l = eval("lambda { |x| #{name} }")
|
14
|
+
@ext_parser = @ext_parser.extend do
|
15
|
+
after(&l)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
When /^I return param "([^\"]*)" in after action$/ do |name|
|
20
|
+
l = eval("lambda { |x| param(#{name}) }")
|
21
|
+
@ext_parser = @ext_parser.extend do
|
22
|
+
after(&l)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
When /^the before action sets @foo="([^\"]*)"$/ do |val|
|
28
|
+
@ext_parser = @ext_parser.extend do
|
29
|
+
before { @foo=val; [] }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
When /^the after action returns @foo$/ do
|
34
|
+
@ext_parser = @ext_parser.extend do
|
35
|
+
after { @foo }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
When /^set with\("([^\"]*)"\) to skip the entry$/ do |key|
|
40
|
+
@ext_parser = @ext_parser.extend do
|
41
|
+
rules do
|
42
|
+
with(key) { skip_entry! }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
data/lib/swissparser.rb
CHANGED
@@ -18,90 +18,12 @@ along with SwissParser. If not, see <http://www.gnu.org/licenses/>.
|
|
18
18
|
=end
|
19
19
|
|
20
20
|
require 'open-uri'
|
21
|
+
require 'swissparser/parsing_context'
|
22
|
+
require 'swissparser/parsing_rules'
|
21
23
|
|
22
24
|
module Swiss
|
23
25
|
|
24
|
-
VERSION = "0.
|
25
|
-
|
26
|
-
# This class defines parsing rules. Its methods
|
27
|
-
# are accessible within the +rules+ section of
|
28
|
-
# a parser definition.
|
29
|
-
class ParsingRules
|
30
|
-
|
31
|
-
attr_reader :separator, :actions
|
32
|
-
|
33
|
-
# *Do* *not* create directly this class but access it
|
34
|
-
# through a +rules+ section in a parser definition.
|
35
|
-
def initialize
|
36
|
-
@actions = { :text => {} }
|
37
|
-
end
|
38
|
-
|
39
|
-
# Sets the entry separator line. Default: "//"
|
40
|
-
def set_separator(string)
|
41
|
-
@separator = string
|
42
|
-
end
|
43
|
-
|
44
|
-
# Defines how to parse a line starting with +key+. The +proc+
|
45
|
-
# takes two arguments:
|
46
|
-
# * the rest of the line
|
47
|
-
# * the entry object
|
48
|
-
def with( key, &proc )
|
49
|
-
@actions[key] = proc
|
50
|
-
end
|
51
|
-
|
52
|
-
# Defines how to parse a line without key coming *after*
|
53
|
-
# a specified key. The +proc+ takes two arguments:
|
54
|
-
# * the rest of the line
|
55
|
-
# * the entry object
|
56
|
-
def with_text_after( key, &proc )
|
57
|
-
@actions[:text][key] = proc
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
# Methods of this class are accessible to rules and actions.
|
63
|
-
# Methods defined in +helpers+ block are added to this class.
|
64
|
-
class ParsingContext
|
65
|
-
|
66
|
-
def initialize(parameters)
|
67
|
-
@params = parameters
|
68
|
-
end
|
69
|
-
|
70
|
-
# Retrieves a parsing parameter by key. Returns nil if
|
71
|
-
# there is no parameter with the provided key.
|
72
|
-
def param( key )
|
73
|
-
@params[key]
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
module InstanceExecHelper #:nodoc:
|
78
|
-
end
|
79
|
-
|
80
|
-
include InstanceExecHelper
|
81
|
-
|
82
|
-
#Method instance_exec exists since version 1.9
|
83
|
-
if RUBY_VERSION < "1.9"
|
84
|
-
#Used to execute rules and action using the ParsingContext as context
|
85
|
-
#Stolen from http://eigenclass.org/hiki/bounded+space+instance_exec
|
86
|
-
def instance_exec(*args, &block)
|
87
|
-
begin
|
88
|
-
old_critical, Thread.critical = Thread.critical, true
|
89
|
-
n = 0
|
90
|
-
n += 1 while respond_to?(mname="__instance_exec#{n}")
|
91
|
-
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
92
|
-
ensure
|
93
|
-
Thread.critical = old_critical
|
94
|
-
end
|
95
|
-
begin
|
96
|
-
ret = send(mname, *args)
|
97
|
-
ensure
|
98
|
-
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
|
99
|
-
end
|
100
|
-
ret
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|
26
|
+
VERSION = "0.11.0"
|
105
27
|
|
106
28
|
|
107
29
|
# Parser for a typical bioinformatic flat file.
|
@@ -182,14 +104,8 @@ module Swiss
|
|
182
104
|
r = ParsingRules.new
|
183
105
|
r.instance_eval(&proc)
|
184
106
|
r.actions.each do |k,v|
|
185
|
-
if k == :text
|
186
|
-
next
|
187
|
-
end
|
188
107
|
@actions[k] = v
|
189
108
|
end
|
190
|
-
r.actions[:text].each do |k,v|
|
191
|
-
@actions[:text][k] = v
|
192
|
-
end
|
193
109
|
if r.separator
|
194
110
|
@separator = r.separator
|
195
111
|
end
|
@@ -197,7 +113,6 @@ module Swiss
|
|
197
113
|
|
198
114
|
|
199
115
|
|
200
|
-
|
201
116
|
# Extends an existing parser by allowing to redefine rules. The
|
202
117
|
# changes in the new parser simply replace the original defintions.
|
203
118
|
# After extension, the new parser is independent of the original one,
|
@@ -243,26 +158,30 @@ module Swiss
|
|
243
158
|
# value specified in the +after+ block. By default, it returns an
|
244
159
|
# array containing _entry_ objects.
|
245
160
|
def parse( data, params={} )
|
246
|
-
@ctx =
|
247
|
-
|
248
|
-
@helpers.each do |name, proc|
|
249
|
-
helperModule.send( :define_method, name, proc )
|
250
|
-
end
|
251
|
-
@ctx.extend( helperModule )
|
161
|
+
@ctx = init_context( params )
|
162
|
+
state = :begin
|
252
163
|
container = @ctx.instance_exec( &@before )
|
253
164
|
entry = @ctx.instance_exec( &@begin )
|
254
165
|
data.each_line do |line|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
166
|
+
if @ctx.should_skip?
|
167
|
+
if line == @separator
|
168
|
+
state = :end
|
169
|
+
entry = init_entry
|
170
|
+
end
|
171
|
+
else
|
172
|
+
state = parse_line( line, entry )
|
173
|
+
if state == :end
|
174
|
+
@ctx.instance_exec( entry, container, &@end )
|
175
|
+
entry = init_entry
|
176
|
+
end
|
259
177
|
end
|
260
178
|
end
|
179
|
+
if state == :parsing
|
180
|
+
raise("No separator at end of file")
|
181
|
+
end
|
261
182
|
@ctx.instance_exec( container, &@after )
|
262
183
|
end
|
263
184
|
|
264
|
-
private
|
265
|
-
|
266
185
|
PROTOTYPE = Parser.new
|
267
186
|
PROTOTYPE.instance_eval do
|
268
187
|
before { || [] }
|
@@ -270,6 +189,24 @@ module Swiss
|
|
270
189
|
finish_entry {|e,c| c << e }
|
271
190
|
after {|c| c }
|
272
191
|
end
|
192
|
+
PROTOTYPE.freeze
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def init_entry()
|
197
|
+
@ctx.reset_skip
|
198
|
+
@ctx.instance_exec( &@begin )
|
199
|
+
end
|
200
|
+
|
201
|
+
def init_context(params)
|
202
|
+
ctx = ParsingContext.new( params )
|
203
|
+
helperModule = Module.new
|
204
|
+
@helpers.each do |name, proc|
|
205
|
+
helperModule.send( :define_method, name, proc )
|
206
|
+
end
|
207
|
+
ctx.extend( helperModule )
|
208
|
+
ctx
|
209
|
+
end
|
273
210
|
|
274
211
|
|
275
212
|
def parse_line( line, holder )
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Swiss
|
2
|
+
|
3
|
+
# Methods of this class are accessible to rules and actions.
|
4
|
+
# Methods defined in +helpers+ block are added to this class.
|
5
|
+
class ParsingContext
|
6
|
+
|
7
|
+
def initialize(parameters)
|
8
|
+
@params = parameters
|
9
|
+
@skip = false
|
10
|
+
end
|
11
|
+
|
12
|
+
# Retrieves a parsing parameter by key. Returns nil if
|
13
|
+
# there is no parameter with the provided key.
|
14
|
+
def param( key )
|
15
|
+
@params[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def skip_entry!()
|
19
|
+
@skip = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def should_skip?()
|
23
|
+
@skip
|
24
|
+
end
|
25
|
+
|
26
|
+
def reset_skip()
|
27
|
+
@skip = false
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
module InstanceExecHelper #:nodoc:
|
32
|
+
end
|
33
|
+
|
34
|
+
include InstanceExecHelper
|
35
|
+
|
36
|
+
#Method instance_exec exists since version 1.9
|
37
|
+
if RUBY_VERSION < "1.9"
|
38
|
+
#Used to execute rules and action using the ParsingContext as context
|
39
|
+
#Stolen from http://eigenclass.org/hiki/bounded+space+instance_exec
|
40
|
+
def instance_exec(*args, &block)
|
41
|
+
begin
|
42
|
+
old_critical, Thread.critical = Thread.critical, true
|
43
|
+
n = 0
|
44
|
+
n += 1 while respond_to?(mname="__instance_exec#{n}")
|
45
|
+
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
46
|
+
ensure
|
47
|
+
Thread.critical = old_critical
|
48
|
+
end
|
49
|
+
begin
|
50
|
+
ret = send(mname, *args)
|
51
|
+
ensure
|
52
|
+
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
|
53
|
+
end
|
54
|
+
ret
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Swiss
|
2
|
+
|
3
|
+
# This class defines parsing rules. Its methods
|
4
|
+
# are accessible within the +rules+ section of
|
5
|
+
# a parser definition.
|
6
|
+
class ParsingRules
|
7
|
+
|
8
|
+
attr_reader :separator, :actions
|
9
|
+
|
10
|
+
# *Do* *not* create directly this class but access it
|
11
|
+
# through a +rules+ section in a parser definition.
|
12
|
+
def initialize
|
13
|
+
@actions = { :text => {} }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Sets the entry separator line. Default: "//"
|
17
|
+
def set_separator(string)
|
18
|
+
@separator = string
|
19
|
+
end
|
20
|
+
|
21
|
+
# Defines how to parse a line starting with +key+. The +proc+
|
22
|
+
# takes two arguments:
|
23
|
+
# * the rest of the line
|
24
|
+
# * the entry object
|
25
|
+
def with( key, &proc )
|
26
|
+
@actions[key] = proc
|
27
|
+
end
|
28
|
+
|
29
|
+
# Defines how to parse a line without key coming *after*
|
30
|
+
# a specified key. The +proc+ takes two arguments:
|
31
|
+
# * the rest of the line
|
32
|
+
# * the entry object
|
33
|
+
def with_text_after( key, &proc )
|
34
|
+
@actions[:text][key] = proc
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: swissparser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- paradigmatic
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-16 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -56,11 +56,17 @@ files:
|
|
56
56
|
- examples/tutorial_1.rb
|
57
57
|
- examples/tutorial_2.rb
|
58
58
|
- examples/uniprot_param_demo.rb
|
59
|
+
- features/basic_parsing.feature
|
59
60
|
- features/parser_extension.feature
|
60
|
-
- features/
|
61
|
-
- features/
|
61
|
+
- features/parsing_context.feature
|
62
|
+
- features/polite.feature
|
63
|
+
- features/step_definitions/core.rb
|
64
|
+
- features/step_definitions/definitions.rb
|
65
|
+
- features/step_definitions/extra.rb
|
62
66
|
- lib/swiss_parser.rb
|
63
67
|
- lib/swissparser.rb
|
68
|
+
- lib/swissparser/parsing_context.rb
|
69
|
+
- lib/swissparser/parsing_rules.rb
|
64
70
|
has_rdoc: true
|
65
71
|
homepage: http://github.com/paradigmatic/SwissParser
|
66
72
|
licenses: []
|
@@ -1,103 +0,0 @@
|
|
1
|
-
require 'lib/swissparser'
|
2
|
-
require 'spec/expectations'
|
3
|
-
|
4
|
-
|
5
|
-
Given /^a simple parser$/ do
|
6
|
-
@simple_parser = Swiss::Parser.define do
|
7
|
-
rules do
|
8
|
-
with("XX") {|c,e| e["XX"] = c}
|
9
|
-
with("YY") {|c,e| e["YY"] = c}
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
Given /^input data$/ do |string|
|
15
|
-
@data = string
|
16
|
-
end
|
17
|
-
|
18
|
-
When /^I extend it$/ do
|
19
|
-
@ext_parser = @simple_parser.extend {}
|
20
|
-
end
|
21
|
-
|
22
|
-
When /^I replace with\("([^\"]*)"\) to return always '([^\']*)'$/ do |key,out|
|
23
|
-
@ext_parser = @ext_parser.extend do
|
24
|
-
rules do
|
25
|
-
with( key ) {|c,e| e[key] = out }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
When /^I replace with\("([^\"]*)"\) to do nothing$/ do |key|
|
31
|
-
@ext_parser = @ext_parser.extend do
|
32
|
-
rules do
|
33
|
-
with( key ) {|c,e| }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
When /^I replace with_text_after\("([^\"]*)"\) to return always '([^\']*)'$/ do |key,out|
|
39
|
-
text_key = "txt-#{key}"
|
40
|
-
@ext_parser = @ext_parser.extend do
|
41
|
-
rules do
|
42
|
-
with_text_after( key ) {|c,e| e[text_key] = out }
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
When /^I set the separator to '([^\']*)'$/ do |sep|
|
48
|
-
@ext_parser = @ext_parser.extend do
|
49
|
-
rules do
|
50
|
-
set_separator( sep )
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
When /^I define '([^\']*)' helper$/ do |name|
|
56
|
-
@ext_parser = @ext_parser.extend do
|
57
|
-
helper(name.to_sym) do
|
58
|
-
name
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
When /^I call '([^\']*)' helper in after action$/ do |name|
|
64
|
-
l = eval("lambda { |x| #{name} }")
|
65
|
-
@ext_parser = @ext_parser.extend do
|
66
|
-
after(&l)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
When /^I return param "([^\"]*)" in after action$/ do |name|
|
71
|
-
l = eval("lambda { |x| param(#{name}) }")
|
72
|
-
@ext_parser = @ext_parser.extend do
|
73
|
-
after(&l)
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
When /^I call parse with param "([^\"]*)" equal to "([^\"]*)"$/ do |key, val|
|
79
|
-
@result = @ext_parser.parse(@data, key => val)
|
80
|
-
end
|
81
|
-
|
82
|
-
Then /^the result should be "([^\"]*)"$/ do |val|
|
83
|
-
@result.should == val
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
|
-
Then /^the parser should return '([^\']*)'$/ do |val|
|
88
|
-
@ext_parser.parse( @data ).should == val
|
89
|
-
end
|
90
|
-
|
91
|
-
Then /^the parser should return '(\d*)' entries$/ do |num|
|
92
|
-
@ext_parser.parse( @data ).size.should == num.to_i
|
93
|
-
end
|
94
|
-
|
95
|
-
Then /^the extended parser should parse it as the original one$/ do
|
96
|
-
@simple_parser.parse( @data ).should == @ext_parser.parse( @data )
|
97
|
-
end
|
98
|
-
|
99
|
-
|
100
|
-
Then /^the parser should return "([^\"]*)"$/ do |ruby_exp|
|
101
|
-
result = eval(ruby_exp)
|
102
|
-
@ext_parser.parse( @data ).should == result
|
103
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
Feature: User Friendly
|
2
|
-
SwissParser is nice to use.
|
3
|
-
|
4
|
-
Background:
|
5
|
-
Given input data
|
6
|
-
"""
|
7
|
-
XX a1
|
8
|
-
YY b1
|
9
|
-
c1
|
10
|
-
//
|
11
|
-
XX a1
|
12
|
-
YY b2
|
13
|
-
c2
|
14
|
-
//
|
15
|
-
"""
|
16
|
-
|
17
|
-
Scenario: Helper Method
|
18
|
-
Given a simple parser
|
19
|
-
When I extend it
|
20
|
-
And I define 'foo' helper
|
21
|
-
And I call 'foo' helper in after action
|
22
|
-
Then the parser should return 'foo'
|
23
|
-
|
24
|
-
Scenario: Helper Method
|
25
|
-
Given a simple parser
|
26
|
-
When I extend it
|
27
|
-
And I return param "foo" in after action
|
28
|
-
And I call parse with param "foo" equal to "bar"
|
29
|
-
Then the result should be "bar"
|