Class: Parslet::Atoms::Repetition

Inherits:
Base
  • Object
show all
Defined in:
lib/parslet/atoms/repetition.rb,
lib/parslet/atoms/visitor.rb

Overview

Matches a parslet repeatedly.

Example:

str('a').repeat(1,3)  # matches 'a' at least once, but at most three times
str('a').maybe        # matches 'a' if it is present in the input (repeat(0,1))

Constant Summary

Constants included from Precedence

Precedence::ALTERNATE, Precedence::BASE, Precedence::LOOKAHEAD, Precedence::OUTER, Precedence::REPETITION, Precedence::SEQUENCE

Instance Attribute Summary collapse

Attributes inherited from Base

#label

Instance Method Summary collapse

Methods inherited from Base

#apply, #cached?, #inspect, #parse, #parse_with_debug, precedence, #setup_and_apply, #to_s

Methods included from CanFlatten

#flatten, #flatten_repetition, #flatten_sequence, #foldl, #merge_fold, #warn_about_duplicate_keys

Methods included from DSL

#>>, #absent?, #as, #capture, #ignore, #maybe, #present?, #repeat, #|

Constructor Details

#initialize(parslet, min, max, tag = :repetition) ⇒ Repetition

Returns a new instance of Repetition.

Raises:

  • (ArgumentError)


11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/parslet/atoms/repetition.rb', line 11

def initialize(parslet, min, max, tag=:repetition)
  super()

  raise ArgumentError, 
    "Asking for zero repetitions of a parslet. (#{parslet.inspect} repeating #{min},#{max})" \
    if max == 0


  @parslet = parslet
  @min = min
  @max = max
  @tag = tag
end

Instance Attribute Details

#maxObject (readonly)

Returns the value of attribute max.



10
11
12
# File 'lib/parslet/atoms/repetition.rb', line 10

def max
  @max
end

#minObject (readonly)

Returns the value of attribute min.



10
11
12
# File 'lib/parslet/atoms/repetition.rb', line 10

def min
  @min
end

#parsletObject (readonly)

Returns the value of attribute parslet.



10
11
12
# File 'lib/parslet/atoms/repetition.rb', line 10

def parslet
  @parslet
end

Instance Method Details

#accept(visitor) ⇒ Object

Call back visitors #visit_repetition method. See parslet/export for an example.



51
52
53
# File 'lib/parslet/atoms/visitor.rb', line 51

def accept(visitor)
  visitor.visit_repetition(@tag, min, max, parslet)
end

#error_msgsObject



25
26
27
28
29
30
# File 'lib/parslet/atoms/repetition.rb', line 25

def error_msgs
  @error_msgs ||= {
    minrep: "Expected at least #{min} of #{parslet.inspect}",
    unconsumed: 'Extra input after last repetition'
  }
end

#to_s_inner(prec) ⇒ Object



80
81
82
83
84
85
# File 'lib/parslet/atoms/repetition.rb', line 80

def to_s_inner(prec)
  minmax = "{#{min}, #{max}}"
  minmax = '?' if min == 0 && max == 1

  parslet.to_s(prec) + minmax
end

#try(source, context, consume_all) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/parslet/atoms/repetition.rb', line 32

def try(source, context, consume_all)
  occ = 0
  accum = [@tag]   # initialize the result array with the tag (for flattening)
  start_pos = source.pos
  
  break_on = nil
  loop do
    success, value = parslet.apply(source, context, false)

    break_on = value
    break unless success

    occ += 1
    accum << value
    
    # If we're not greedy (max is defined), check if that has been reached. 
    return succ(accum) if max && occ>=max
  end
  
  # Last attempt to match parslet was a failure, failure reason in break_on.
  
  # Greedy matcher has produced a failure. Check if occ (which will
  # contain the number of successes) is >= min.
  return context.err_at(
    self, 
    source, 
    error_msgs[:minrep],
    start_pos, 
    [break_on]) if occ < min
    
  # consume_all is true, that means that we're inside the part of the parser
  # that should consume the input completely. Repetition failing here means
  # probably that we didn't. 
  #
  # We have a special clause to create an error here because otherwise
  # break_on would get thrown away. It turns out, that contains very
  # interesting information in a lot of cases. 
  #
  return context.err(
    self, 
    source, 
    error_msgs[:unconsumed], 
    [break_on]) if consume_all && source.chars_left>0
    
  return succ(accum)
end