cannonbol 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,678 @@
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
+
26
+ 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, s=[])
182
+ ignore_case = needle.ignore_case
183
+ needle.ignore_case = true
184
+ s = @pattern._match?(needle, *s)
185
+ needle.ignore_case = ignore_case
186
+ return [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 RUBY_ENGINE == '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 RUBY_ENGINE == '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
+ end