cannonbol 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a648c8cfd875765efac54d699ee04acbbcd7f422
4
- data.tar.gz: 47910f41c8d1cfb8953247fa8b788a09ce7d20fd
3
+ metadata.gz: a8b090116c27ac8cf008c2a0b54b5aa2eb259bf4
4
+ data.tar.gz: 6899f4b674be76248a133b664870632eae5d48bd
5
5
  SHA512:
6
- metadata.gz: 7dea1273241b4517b6542de8f557f2401fcd6880330d5662edc533428e3878fc15680bfb8a5ae0537c21cfa2a7a62a85dc12e080a74922e8fdbd5a053492d66d
7
- data.tar.gz: 14bd53603b2c261ea45cc25f9593fc38c7437aa040eef859651e0b428e40525761b5842237996818f27fecf2d6e45db599fc1dfd5711daa2e216cc0704a57540
6
+ metadata.gz: 9e820fa3e6b2758aa2e5fd67d7b9d24c6482bc96ad33b4b2763b071f3d73d5b8944fe5da77e56bdfd1fb03df303f4a13c024d3cb0884745fac100ddc4429baec
7
+ data.tar.gz: 201b97e9cdf60edbf9b3c5b84d9801b7883f06371f2a23c234616c4e0662ccc7d8eb716d9e52fb01cdf422ce458377881fc71fe550e8e33cff5dfca7d3d5f06a
data/README.md CHANGED
@@ -9,6 +9,7 @@ CannonBol is a ruby DSL for patten matching based on SNOBOL4 and SPITBOL.
9
9
  * Complete SNOBOL4 + SPITBOL extensions!
10
10
  * Based on the well documented, proven SNOBOL4 language!
11
11
  * Simple syntax looks great within your ruby code!
12
+ * Tested with ruby 1.9.3, 2.1.1 and [Opal](www.opalrb.org)
12
13
 
13
14
  ## Installation
14
15
 
@@ -34,28 +35,31 @@ Strings, Regexes and primitives are combined using & (concatenation) and | (alte
34
35
 
35
36
  Here is a simple pattern that matches a simple noun clause:
36
37
 
37
- ("a" | "the") & /\s+/ & ("boy" | "girl")
38
+ > ("a" | "the") & /\s+/ & ("boy" | "girl")
38
39
 
39
- So we will match either "a" or "the" followed white space and then by "boy or "girl". Okay! Lets use it!
40
+ This will match either "a" or "the" followed white space and then by "boy or "girl". Okay! Lets use it!
40
41
 
41
- ("a" | "the") & /\s+/ & ("boy" | "girl").match?("he saw a boy going home")
42
+ > ("a" | "the") & /\s+/ & ("boy" | "girl").match?("he saw a boy going home")
42
43
  => "a boy"
43
- ("a" | "the") & /\s+/ & ("boy" | "girl").match?("he saw a big boy going home")
44
+ > ("a" | "the") & /\s+/ & ("boy" | "girl").match?("he saw a big boy going home")
44
45
  => nil
45
46
 
46
- Now let's save the pieces of the match using the capture? (pronounced _capture IF_) method:
47
+ Now let's save the pieces of the match using the `capture?` (pronounced _capture IF_) method:
47
48
 
48
- article, noun = nil, nil
49
- pattern = ("a" | "the").capture? { |m| article = m } & /\s+/ & ("boy" | "girl").capture? { |m| noun = m }
50
- pattern.match?("he saw the girl going home")
51
- noun
49
+ > article, noun = nil, nil;
50
+ * pattern = ("a" | "the").capture? { |m| article = m } & /\s+/ & ("boy" | "girl").capture? { |m| noun = m };
51
+ * pattern.match?("he saw the girl going home")
52
+ => the girl
53
+ > noun
52
54
  => girl
53
- article
55
+ > article
54
56
  => the
55
57
 
56
- The capture? method and its friend capture! (pronounced _capture NOW_) have many powerful features. As shown above it can take a block which is passed the matching substring, _IF the match succeeds_. The other features of the capture method will be detailed [below.](Advanced capture techniques)
58
+ The `capture?` method and its friend `capture!` (pronounced _capture NOW_) have many powerful features.
59
+ As shown above it can take a block which is passed the matching substring, _IF the match succeeds_.
60
+ The other features of the capture method will be detailed [below.](Advanced capture techniques)
57
61
 
58
- Arrays can be turned into patterns using the match_any and match_all methods:
62
+ Arrays can be turned into patterns using the `match_any` and `match_all` methods:
59
63
 
60
64
  ARTICLES = ["a", "the"]
61
65
  NOUNS = ["boy", "girl", "dog", "cat"]
@@ -64,7 +68,7 @@ Arrays can be turned into patterns using the match_any and match_all methods:
64
68
  [ARTICLES.match_any, [WS, [WS, ADJECTIVES.match_any, WS].match_all].match_any, NOUNS.match_any].match_all
65
69
 
66
70
  This is equivilent to
67
-
71
+
68
72
  ("a" | "the") & (WS | (WS & ("big" | "small" | "fierce" | "friendly") & WS)) & ("boy" | "girl" | "dog" | "cat")
69
73
 
70
74
  ### match? options
@@ -80,20 +84,30 @@ replace_with | nil | When a non-falsy value is supplied, the value will replace
80
84
 
81
85
  Example of replace with:
82
86
 
83
- "hello".match?("She said hello!")
87
+ > "hello".match?("She said hello!")
84
88
  => hello
85
- "hello".match?("She said hello!", replace_with => "goodby")
89
+ > "hello".match?("She said hello!", replace_with => "goodby")
86
90
  => She said goodby!
91
+
92
+ #### Ignore case on a subpattern
93
+
94
+ Sometimes its useful to run the matcher in the default case sensitive mode, and only turn off matching for one part of the pattern. To do this
95
+ prefix a subpattern with a "-". For example
87
96
 
97
+ > (-"GIRL" | "boy").match?("A big girl!")
98
+ => girl
99
+ > (-"GIRL" | "boy").match?("A big BOY!")
100
+ => nil
101
+
88
102
  ### Patterns, Subjects, Cursors, Alternatives, and Backtracking
89
103
 
90
104
  A pattern is an object that responds to the match? method. Cannonbol adds the match? method to Ruby strings, and regexes, and provides a number of _primitive_ patterns. A pattern can be combined with another pattern using the &, and | operators. There are also several primitive patterns that take a pattern and create a new pattern. Here are some example patterns:
91
105
 
92
- "hello" # matches any string containing hello
93
- /\s+/ # matches one or more white space characters
106
+ "hello" # matches any string containing hello
107
+ /\s+/ # matches one or more white space characters
94
108
  "hello" & /\s+/ & "there" # matches "hello" and "there" seperated by white space
95
- "hello" | "goodby" # matches EITHER "hello" or "there"
96
- ARB # a primitive pattern that matches anything (similar to /.*/)
109
+ "hello" | "goodby" # matches EITHER "hello" or "there"
110
+ ARB # a primitive pattern that matches anything (similar to /.*/)
97
111
  ("hello" | "goodby") & ARB & "Fred" # matches "hello" or "goodby" followed by any characters and finally "Fred"
98
112
 
99
113
  Patterns are just objects, so they can be assigned to variables:
@@ -270,18 +284,19 @@ We can use this to clean up the palindrome pattern a little bit:
270
284
 
271
285
  Another way to get the capture variables is to interogate the value returned by match?. The value returned by match? is a subclass of string, that has some extra methods. One of these is the captured method which gives a hash of all the captured variables. For example:
272
286
 
273
- ("dog" | "cat").capture?(:pet).match?("He had a dog named Spot.").captured[:pet]
287
+ > ("dog" | "cat").capture?(:pet).match?("He had a dog named Spot.").captured[:pet]
274
288
  => dog
275
289
 
276
290
  You can also give a block to the match? method which will be called whether the block passes or not. For example:
277
291
 
278
- ("dog" | "cat").capture?(:pet).match?("He had a dog named Spot."){ |match| match.captured[:pet] if match}
292
+ > ("dog" | "cat").capture?(:pet).match?("He had a dog named Spot."){ |match| match.captured[:pet] if match}
279
293
  => dog
280
294
 
281
295
  The match? block can also explicitly name any capture variables you need to get the values of. So for example:
282
296
 
283
- pet_data = (POS(0) & ARBNO(("big" | "small").capture?(:size) | ("dog" | "cat").capture?(:pet) | LEN(1)) & RPOS(0))
284
- pet_data.match?("He has a big dog!") { |m, pet, size| "type of pet: #{pet.upcase}, size: #{size.upcase}"}
297
+ > pet_data = (POS(0) & ARBNO(("big" | "small").capture?(:size) | ("dog" | "cat").capture?(:pet) | LEN(1)) & RPOS(0))
298
+ => #<Cannonbol::Concat .... etc
299
+ > pet_data.match?("He has a big dog!") { |m, pet, size| "type of pet: #{pet.upcase}, size: #{size.upcase}"}
285
300
  => type of pet: DOG, size: BIG
286
301
 
287
302
  If the match? block mentions capture variables that were not assigned in the match they get nil.
@@ -289,14 +304,14 @@ If the match? block mentions capture variables that were not assigned in the mat
289
304
  #### Initializing capture variables
290
305
 
291
306
  When used as a parameter to a primitve the capture variable may be given an initial value. For example:
292
-
307
+
293
308
  LEN(baz: 12)
294
309
 
295
- would match LEN(12) if :baz had not yet been set.
310
+ would match `LEN(12)` if :baz had not yet been set.
296
311
 
297
312
  A second way to initialize (or update capture variables) is to combine capture variables with a capture block like this:
298
313
 
299
- some_pattern.capture!(:baz) { |match, position, baz| baz || position * 2 } initializes :baz to position * 2
314
+ some_pattern.capture!(:baz) { |match, position, baz| baz || position * 2 } # initializes :baz to position * 2
300
315
 
301
316
  If a symbol is specified in a capture!, and there is a block, then the symbol will be set to the value returned by the block.
302
317
 
@@ -349,15 +364,13 @@ The difference is that FENCE will fail the whole match, but FENCE(pattern) will
349
364
 
350
365
  These can be used together to do some interesting things. For example
351
366
 
352
- pattern = POS(0) & SUCCEED & (FENCE(TAB(n: 1).capture!(:n) { |m, p, n| puts m; p+1 } | ABORT)) & FAIL
353
- pattern.match?("abcd")
354
-
355
- prints
356
-
367
+ > pattern = POS(0) & SUCCEED & (FENCE(TAB(n: 1).capture!(:n) { |m, p, n| puts m; p+1 } | ABORT)) & FAIL;
368
+ * pattern.match?("abcd")
357
369
  a
358
370
  ab
359
371
  abc
360
372
  abcd
373
+ => nil
361
374
 
362
375
  The SUCCEED and FAIL primitives keep forcing the matcher to retry. Eventually the TAB will fail causing the ABORT alternative to execute the matcher.
363
376
 
@@ -397,19 +410,24 @@ Cannonbol can be used to easily translate the email BNF spec into an email addre
397
410
 
398
411
  So for example we can even parse an obscure email with groups and routes
399
412
 
400
- email = 'here is my "big fat \\\n groupen" : someone@catprint.com, Fred Nurph<@sub1.sub2@sub3.sub4:fred.nurph@catprint.com>;'
401
- match = address.match?(email)
402
- match.captured[:group_mailboxes].first.captured[:mailbox]
413
+ > email = 'here is my "big fat \\\n groupen" : someone@catprint.com, Fred Nurph<@sub1.sub2@sub3.sub4:fred.nurph@catprint.com>;'
414
+ => here is my "big fat \\\n groupen" : someone@catprint.com, Fred Nurph<@sub1.sub2@sub3.sub4:fred.nurph@catprint.com>;
415
+ > match = address.match?(email)
416
+ => here is my "big fat \\\n groupen" : someone@catprint.com, Fred Nurph<@sub1.sub2@sub3.sub4:fred.nurph@catprint.com>;
417
+ > match.captured[:group_mailboxes].first.captured[:mailbox]
403
418
  => someone@catprint.com
404
- match.captured[:group_name]
419
+ > match.captured[:group_name]
405
420
  => here is my "big fat \\\n groupen
406
421
 
407
422
 
408
423
  ## Development
409
424
 
410
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
425
+ After checking out the repo, run `bundle install` to install dependencies.
426
+
427
+ ### Specs
411
428
 
412
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
429
+ Run `bundle exec rspec` to run the tests on your server environment
430
+ Run `bundle exec rake rackup` and then point your browser to your machine to run the tests in the opal
413
431
 
414
432
  ## Contributing
415
433
 
data/Rakefile CHANGED
@@ -1,3 +1,3 @@
1
- require "bundler/gem_tasks"
2
-
3
- Dir.glob('tasks/**/*.rake').each(&method(:import))
1
+ require 'bundler'
2
+ Bundler.require
3
+ Bundler::GemHelper.install_tasks
@@ -33,4 +33,6 @@ Simple syntax looks great alongside ruby!
33
33
  spec.add_development_dependency "bundler", "~> 1.8"
34
34
  spec.add_development_dependency "rake", "~> 10.0"
35
35
  spec.add_development_dependency "rspec"
36
+ spec.add_development_dependency "opal-rspec"
37
+ spec.add_development_dependency "opal"
36
38
  end
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ Bundler.require
3
+
4
+ require 'opal-rspec'
5
+ Opal.append_path File.expand_path('../spec', __FILE__)
6
+
7
+ run Opal::Server.new { |s|
8
+ s.main = 'opal/rspec/sprockets_runner'
9
+ s.append_path 'spec'
10
+ s.debug = false
11
+ s.index_path = 'spec/index.html.erb'
12
+ }
13
+
@@ -1,678 +1,9 @@
1
- module Cannonbol
2
-
3
- class MatchFailed < Exception; end
4
-
5
- class MatchString < String
6
-
7
- attr_reader :captured
8
- attr_reader :match_start
9
- attr_reader :match_end
10
-
11
- def initialize(string, match_start, match_end, captured)
12
- @cannonbol_string = string
13
- @match_start = match_start
14
- @match_end = match_end
15
- @captured = captured.dup
16
- super(@match_end < 0 ? "" : string[@match_start..@match_end])
17
- end
18
-
19
- def replace_match_with(s)
20
- before_match = ""
21
- before_match = @cannonbol_string[0..@match_start-1] if @match_start > 0
22
- after_match = @cannonbol_string[@match_end+1..-1] || ""
23
- before_match + s + after_match
24
- end
25
-
1
+ require_relative 'cannonbol/cannonbol'
2
+ require_relative 'cannonbol/version'
3
+ unless RUBY_ENGINE == 'opal'
4
+ begin
5
+ require 'opal'
6
+ Opal.append_path File.expand_path('..', __FILE__).untaint
7
+ rescue LoadError
26
8
  end
27
-
28
- class Needle
29
-
30
- attr_reader :cursor
31
- attr_reader :string
32
- attr_accessor :captures
33
- attr_accessor :match_failed
34
- attr_accessor :ignore_case
35
-
36
- def initialize(string)
37
- @string = string
38
- end
39
-
40
- def thread(pattern, opts = {}, &match_block)
41
- @captures = {}
42
- anchor = opts[:anchor]
43
- raise_error = opts[:raise_error]
44
- replace_with = opts[:replace_match_with]
45
- ignore_case = opts[:ignore_case]
46
- @cursor = -1
47
- match = nil
48
- begin
49
- while !match and !match_failed and @cursor < @string.length-1
50
- @cursor += 1
51
- @starting_character = nil
52
- @success_blocks = []
53
- @ignore_case = ignore_case
54
- match = pattern._match?(self)
55
- break if !match and anchor
56
- end
57
- rescue MatchFailed
58
- end
59
- if match
60
- @success_blocks.each(&:call)
61
- match = MatchString.new(@string, @starting_character || @cursor, @cursor-1, @captures)
62
- else
63
- raise MatchFailed if raise_error
64
- end
65
- if match_block
66
- match = match_block.call(*([match] + (match_block.parameters[1..-1] || []).collect { |param| @captures[param[1].to_sym] }))
67
- elsif replace_with
68
- match = match.replace_match_with(replace_with)
69
- end
70
- match
71
- end
72
-
73
- def capture(name, value)
74
- @captures[name.to_sym] = value if name
75
- value
76
- end
77
-
78
-
79
- def remaining_string
80
- @string[@cursor..-1]
81
- end
82
-
83
- def push(length, &success_block)
84
- thread_state = [@starting_character, @cursor, @success_blocks.dup, @ignore_case]
85
- @starting_character ||= @cursor
86
- @cursor += length
87
- @success_blocks << success_block if success_block
88
- thread_state
89
- end
90
-
91
- def pull(thread_state)
92
- @starting_character, @cursor, @success_blocks, @ignore_case = thread_state if thread_state
93
- nil
94
- end
95
-
96
- end
97
-
98
- module Operators
99
-
100
- def _match?(needle, *args, &block)
101
- return if needle.match_failed
102
- __match?(needle, *args, &block)
103
- end
104
-
105
- def match?(s, opts = {}, &match_block)
106
- Needle.new(s).thread(self, opts, &match_block)
107
- end
108
-
109
- def |(pattern)
110
- Choose.new(self, pattern)
111
- end
112
-
113
- def &(pattern)
114
- Concat.new(self, pattern)
115
- end
116
-
117
- def -@
118
- CaseSensitiveOff.new(self)
119
- end
120
-
121
- def capture?(opts = {}, &block)
122
- OnSuccess.new(self, opts, &block)
123
- end
124
-
125
- def capture!(opts = {}, &block)
126
- OnMatch.new(self, opts, &block)
127
- end
128
-
129
- end
130
-
131
- class Pattern
132
-
133
- include Operators
134
-
135
- def __match?(needle)
136
- []
137
- end
138
-
139
- end
140
-
141
- class Choose < Pattern
142
-
143
- def __match?(needle, i = 0, s = [])
144
- while i < @params.length
145
- s = @params[i]._match?(needle, *s)
146
- return [i, s] if s
147
- s = []
148
- i += 1
149
- end
150
- nil
151
- end
152
-
153
- def initialize(p1, p2)
154
- @params = [p1, p2]
155
- end
156
-
157
- end
158
-
159
- class Concat < Pattern
160
-
161
- def __match?(needle, i = 0, s = [])
162
- while i < @params.length and i >= 0
163
- s[i] = @params[i]._match?(needle, *(s[i] || []))
164
- i = s[i] ? i+1 : i-1
165
- end
166
- [i-1, s] if i == @params.length
167
- end
168
-
169
- def initialize(p1, p2)
170
- @params = [p1, p2]
171
- end
172
-
173
- end
174
-
175
- class CaseSensitiveOff < Pattern
176
-
177
- def initialize(pattern)
178
- @pattern = pattern
179
- end
180
-
181
- def __match?(needle, thread=nil, s=[])
182
- needle.pull(thread)
183
- thread = needle.push(0)
184
- needle.ignore_case = true
185
- s = @pattern._match?(needle, *s)
186
- return [thread, s] if s
187
- end
188
-
189
- end
190
-
191
- class OnSuccess < Pattern
192
-
193
- def initialize(pattern, opts, &block)
194
- if opts.class == Hash
195
- if opts.first
196
- @capture_name = opts.first.first
197
- @initial_capture_value = opts.first.last
198
- end
199
- else
200
- @capture_name = opts
201
- end
202
- @pattern = pattern
203
- @block = block
204
- end
205
-
206
- def __match?(needle, thread_state = nil, starting_cursor = nil, s=[])
207
- needle.pull(thread_state)
208
- starting_cursor ||= needle.cursor
209
- if s = @pattern._match?(needle, *s)
210
- ending_cursor = needle.cursor-1
211
- push = needle.push(0) do
212
- match_string = MatchString.new(needle.string, starting_cursor, ending_cursor, needle.captures)
213
- capture_value = @capture_name && (needle.captures.has_key?(@capture_name) ? needle.captures[@capture_name] : @initial_capture_value)
214
- if @block
215
- match_string = @block.call(match_string, ending_cursor+1, capture_value)
216
- elsif capture_value.class == Array
217
- match_string = capture_value + [match_string]
218
- end
219
- needle.capture(@capture_name, match_string)
220
- end
221
- [ push, starting_cursor, s ]
222
- end
223
- end
224
-
225
- end
226
-
227
- class OnMatch < OnSuccess
228
-
229
- def __match?(needle, starting_cursor = nil, s=[])
230
- starting_cursor ||= needle.cursor
231
- if s = @pattern._match?(needle, *s)
232
- match_string = MatchString.new(needle.string, starting_cursor, needle.cursor-1, needle.captures)
233
- capture_value = @capture_name && (needle.captures.has_key?(@capture_name) ? needle.captures[@capture_name] : @initial_capture_value)
234
- match_string = @block.call(match_string, needle.cursor, capture_value) if @block
235
- needle.capture(@capture_name, match_string)
236
- [starting_cursor, s]
237
- end
238
- end
239
-
240
- end
241
-
242
- class Match < Pattern
243
-
244
- def initialize(sub_pattern_or_name = nil, &block)
245
- if block
246
- @block = block
247
- elsif sub_pattern_or_name and sub_pattern_or_name.class == Symbol
248
- @name = sub_pattern_or_name
249
- elsif sub_pattern_or_name and sub_pattern_or_name.respond_to? "_match?"
250
- @pattern = sub_pattern_or_name
251
- elsif sub_pattern_or_name and sub_pattern_or_name.respond_to? "to_s"
252
- @pattern = sub_pattern_or_name.to_s
253
- end
254
- end
255
-
256
- def __match?(needle, pattern = nil, s = [])
257
- pattern ||= if @block
258
- @block.call
259
- elsif @name
260
- needle.captures[@name] || ""
261
- else
262
- @pattern
263
- end
264
- existing_captures = needle.captures.dup
265
- s = pattern._match?(needle, *s)
266
- needle.captures = needle.captures.merge(existing_captures)
267
- [pattern, s] if s
268
- end
269
-
270
- end
271
-
272
- class Rem < Pattern
273
-
274
- def __match?(needle, thread_state = nil)
275
- if thread_state
276
- needle_pull(thread_state)
277
- else
278
- [needle.push(needle.string.length-needle.cursor)]
279
- end
280
- end
281
-
282
- end
283
-
284
- class Arb < Pattern
285
-
286
- def __match?(needle, match_length = 0, thread_state = nil)
287
- needle.pull(thread_state)
288
- if needle.remaining_string.length >= match_length
289
- thread_state = needle.push(match_length)
290
- match_length += 1
291
- [match_length, thread_state]
292
- end
293
- end
294
-
295
- end
296
-
297
- class ParameterizedPattern < Pattern
298
-
299
- def initialize(opts = nil, &block)
300
- if opts.class == Hash
301
- if opts.first
302
- @param_name = opts.first.first
303
- @initial_param_value = opts.first.last
304
- end
305
- else
306
- @initial_param_value = opts
307
- end
308
- @block = block
309
- end
310
-
311
- def self.parameter(name, &post_processor)
312
- @post_processor = post_processor
313
- define_method(name) do |needle|
314
- value = (@param_name && needle.captures.has_key?(@param_name)) ? needle.captures[@param_name] : @initial_param_value
315
- value = @block.call(value) if @block
316
- needle.capture(@param_name, value)
317
- value = post_processor.call(value) if @post_processor
318
- value
319
- end
320
- end
321
-
322
- end
323
-
324
- class Len < ParameterizedPattern
325
-
326
- parameter :len
327
-
328
- def __match?(needle, thread_state = nil)
329
-
330
- if thread_state
331
- needle.pull(thread_state)
332
- else
333
- len_temp = len(needle)
334
- [needle.push(len_temp)] if needle.remaining_string.length >= len_temp
335
- end
336
-
337
- end
338
-
339
- end
340
-
341
- class Pos < ParameterizedPattern
342
-
343
- parameter :pos
344
-
345
- def __match?(needle, matched = nil)
346
- return [true] if needle.cursor == pos(needle) and !matched
347
- end
348
-
349
- end
350
-
351
- class RPos < ParameterizedPattern
352
-
353
- parameter :pos
354
-
355
- def __match?(needle, matched = nil)
356
- return [true] if needle.string.length-needle.cursor == pos(needle) and !matched
357
- end
358
-
359
- end
360
-
361
- class Tab < ParameterizedPattern
362
-
363
- parameter :pos
364
-
365
- def __match?(needle, thread_state = nil)
366
-
367
- if thread_state
368
- needle.pull(thread_state)
369
- else
370
- len = pos(needle) - needle.cursor
371
- [needle.push(len)] if len >= 0 and needle.remaining_string.length >= len
372
- end
373
- end
374
-
375
- end
376
-
377
- class RTab < ParameterizedPattern
378
-
379
- parameter :pos
380
-
381
- def __match?(needle, thread_state = nil)
382
- if thread_state
383
- needle.pull(thread_state)
384
- else
385
- len = (needle.remaining_string.length - pos(needle))
386
- [needle.push(len)] if len >= 0 and needle.remaining_string.length >= len
387
- end
388
- end
389
-
390
- end
391
-
392
- class Any < ParameterizedPattern
393
-
394
- parameter :chars, &:split
395
-
396
- def __match?(needle, thread_state = nil)
397
- if thread_state
398
- needle.pull(thread_state)
399
- elsif chars(needle).include? needle.remaining_string[0..0]
400
- [needle.push(1)]
401
- end
402
- end
403
-
404
- end
405
-
406
- class NotAny < ParameterizedPattern
407
-
408
- parameter :chars, &:split
409
-
410
- def __match?(needle, thread_state = nil)
411
- if thread_state
412
- needle.pull(thread_state)
413
- elsif !(chars(needle).include? needle.remaining_string[0..0])
414
- [needle.push(1)]
415
- end
416
- end
417
-
418
- end
419
-
420
- class Span < ParameterizedPattern
421
-
422
- parameter :chars, &:split
423
-
424
- def __match?(needle, match_length = nil, thread_state = nil)
425
- unless match_length
426
- the_chars, match_length = chars(needle), 0
427
- while needle.remaining_string.length > match_length and the_chars.include? needle.remaining_string[match_length..match_length]
428
- match_length += 1
429
- end
430
- end
431
- needle.pull(thread_state)
432
- if match_length > 0
433
- thread_state = needle.push(match_length)
434
- match_length -= 1
435
- [match_length, thread_state]
436
- end
437
- end
438
-
439
- end
440
-
441
- class Break < ParameterizedPattern
442
-
443
- parameter :chars, &:split
444
-
445
- def __match?(needle, thread_state = nil)
446
- if thread_state
447
- needle.pull(thread_state)
448
- else
449
- the_chars, len = chars(needle), 0
450
- while needle.remaining_string.length > len and !(the_chars.include? needle.remaining_string[len..len])
451
- len += 1
452
- end
453
- [needle.push(len)]
454
- end
455
- end
456
-
457
- end
458
-
459
-
460
- class BreakX < ParameterizedPattern
461
-
462
- parameter :chars, &:split
463
-
464
- def __match?(needle, len = 0, thread_state = nil)
465
- needle.pull(thread_state)
466
- the_chars = chars(needle)
467
- while needle.remaining_string.length > len and !(the_chars.include? needle.remaining_string[len..len])
468
- len += 1
469
- end
470
- [len+1, needle.push(len)] if needle.remaining_string.length >= len
471
- end
472
-
473
- end
474
-
475
- class Arbno < Match
476
-
477
- def __match?(needle, pattern = nil, s = [[]])
478
- return if s.length == 0
479
- if pattern
480
- existing_captures = needle.captures.dup
481
- s[-1] = pattern._match?(needle, *(s.last))
482
- s = s[-1] ? s + [[]] : s[0..-2]
483
- needle.captures = needle.captures.merge(existing_captures)
484
- else
485
- if @block
486
- pattern = @block.call
487
- elsif @name
488
- pattern = needle.captures[@name] || ""
489
- else
490
- pattern = @pattern
491
- end
492
- end
493
- [pattern, s]
494
- end
495
-
496
- end
497
-
498
- class FailPat < Pattern
499
-
500
- def __match?(needle)
501
- end
502
-
503
- end
504
-
505
- class Abort < Pattern
506
-
507
- def __match?(needle)
508
- raise MatchFailed
509
- end
510
-
511
- end
512
-
513
- class Fence < Match
514
-
515
- def __match?(needle, on_backtrack = nil)
516
- if on_backtrack == :fail_match
517
- needle.match_failed = true
518
- return nil
519
- elsif on_backtrack == :return_nil
520
- return nil
521
- elsif @block
522
- pattern = @block.call
523
- elsif @name
524
- pattern = needle.captures[@name] || ""
525
- elsif @pattern
526
- pattern = @pattern
527
- else
528
- return [:fail_match]
529
- end
530
- return [:return_nil] if pattern._match?(needle)
531
- end
532
-
533
- end
534
-
535
- class Succeed < Pattern
536
- def _match?(needle, thread_state = nil)
537
- needle.pull(thread_state)
538
- [needle.push(0)]
539
- end
540
- end
541
-
542
- end
543
-
544
- class String
545
-
546
- include Cannonbol::Operators
547
-
548
- def __match?(needle, thread_state = nil)
549
- if thread_state
550
- needle.pull(thread_state)
551
- elsif self.length == 0 or
552
- (!needle.ignore_case and needle.remaining_string[0..self.length-1] == self) or
553
- (needle.ignore_case and needle.remaining_string[0..self.length-1].upcase == self.upcase)
554
- [needle.push(self.length)]
555
- end
556
- end
557
-
558
- end
559
-
560
- class Regexp
561
-
562
- include Cannonbol::Operators
563
-
564
- def __match?(needle, thread_state = nil)
565
- if defined? Opal
566
- options = ""
567
- options += "m" if `#{self}.multiline`
568
- options += "g" if `#{self}.global`
569
- options += "i" if needle.ignore_case or `#{self}.ignoreCase`
570
- else
571
- options = self.options | (needle.ignore_case ? Regexp::IGNORECASE : 0)
572
- end
573
- @cannonbol_regex ||= Regexp.new("^#{self.source}", options )
574
- if thread_state
575
- needle.pull(thread_state)
576
- elsif m = @cannonbol_regex.match(needle.remaining_string)
577
- [needle.push(m[0].length)]
578
- end
579
- end
580
-
581
- end
582
-
583
- if defined? Opal
584
-
585
- class Proc
586
-
587
- def parameters
588
- /.*function[^(]*\(([^)]*)\)/.match(`#{self}.toString()`)[1].split(",").collect { |param| [:req, param.strip.to_sym]}
589
- end
590
-
591
- end
592
-
593
- end
594
-
595
- module Enumerable
596
-
597
- def match_any
598
- if self.first
599
- self[1..-1].inject(self.first) { |memo, item| memo | item }
600
- else
601
- FAIL
602
- end
603
- end
604
-
605
- def match_all
606
- self.inject("") { |memo, item| memo & item }
607
- end
608
-
609
- end
610
-
611
-
612
- class Object
613
-
614
- REM = Cannonbol::Rem.new
615
-
616
- ARB = Cannonbol::Arb.new
617
-
618
- FAIL = Cannonbol::FailPat.new
619
-
620
- ABORT = Cannonbol::Abort.new
621
-
622
- FENCE = Cannonbol::Fence.new
623
-
624
- SUCCEED = Cannonbol::Succeed.new
625
-
626
- def LEN(p={}, &block)
627
- Cannonbol::Len.new(p, &block)
628
- end
629
-
630
- def POS(p=nil, &block)
631
- Cannonbol::Pos.new(p, &block)
632
- end
633
-
634
- def RPOS(p=nil, &block)
635
- Cannonbol::RPos.new(p, &block)
636
- end
637
-
638
- def TAB(p=nil, &block)
639
- Cannonbol::Tab.new(p, &block)
640
- end
641
-
642
- def RTAB(p=nil, &block)
643
- Cannonbol::RTab.new(p, &block)
644
- end
645
-
646
- def ANY(p=nil, &block)
647
- Cannonbol::Any.new(p, &block)
648
- end
649
-
650
- def NOTANY(p=nil, &block)
651
- Cannonbol::NotAny.new(p, &block)
652
- end
653
-
654
- def SPAN(p=nil, &block)
655
- Cannonbol::Span.new(p, &block)
656
- end
657
-
658
- def BREAK(p=nil, &block)
659
- Cannonbol::Break.new(p, &block)
660
- end
661
-
662
- def BREAKX(p=nil, &block)
663
- Cannonbol::BreakX.new(p, &block)
664
- end
665
-
666
- def MATCH(p=nil, &block)
667
- Cannonbol::Match.new(p, &block)
668
- end
669
-
670
- def ARBNO(p=nil, &block)
671
- Cannonbol::Arbno.new(p, &block)
672
- end
673
-
674
- def FENCE(p=nil, &block)
675
- Cannonbol::Fence.new(p, &block)
676
- end
677
-
678
9
  end