Module: Rsec::Helpers

Defined in:
lib/rsec/helpers.rb

Overview


these are not callable from a parser

Instance Method Summary collapse

Instance Method Details

#lazy(&p) ⇒ Object

@ desc.helper

Lazy parser is constructed when parsing starts. It is useful to reference a parser not defined yet

@ example

parser = lazy{future}
future = 'jim'.r
assert_equal 'jim', parser.parse '12323'

Raises:

  • (ArgumentError)


17
18
19
20
# File 'lib/rsec/helpers.rb', line 17

def lazy &p
  raise ArgumentError, 'lazy() requires a block' unless p
  Lazy[p]
end

#one_of(str, &p) ⇒ Object

@ desc.helper

Parses one of chars in str

@ example

multiplicative = one_of '*/%'
assert_equal '/', multiplicative.parse '/'
assert_equal Rsec::INVALID, actualmultiplicative.parse '+'

Raises:

  • (ArgumentError)


28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/rsec/helpers.rb', line 28

def one_of str, &p
  Rsec.assert_type str, String
  raise ArgumentError, 'str len should > 0' if str.empty?
  one_of_klass =
    if (str.bytesize == str.size) and Rsec.const_defined?(:OneOfByte)
      # for C-ext
      OneOfByte
    else
      OneOf
    end
  one_of_klass[str.dup.freeze].map p
end

#one_of_(str, &p) ⇒ Object

@ desc.helper

See also #one_of#, with leading and trailing optional breakable spaces

@ example

additive = one_of_('+-')
assert_equal '+', additive.parse('  +')

Raises:

  • (ArgumentError)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/rsec/helpers.rb', line 46

def one_of_ str, &p
  Rsec.assert_type str, String
  raise ArgumentError, 'str len should > 0' if str.empty?
  raise ArgumentError, 'str should be ascii' unless str.bytesize == str.size
  raise ArgumentError, 'str should not contain space' if str =~ /\s/
  spaced_one_of_klass =
    if (str.bytesize == str.size) and Rsec.const_defined?(:OneOfByte_)
      # for C-ext
      OneOfByte_
    else
      OneOf_
    end
  spaced_one_of_klass[str.dup.freeze].map p
end

#prim(type, options = {}, &p) ⇒ Object

@ desc.helper

Primitive parser, returns nil if overflow or underflow.
There can be an optional '+' or '-' at the beginning of string except unsinged_int32 | unsinged_int64.
type =
  :double |
  :hex_double |
  :int32 |
  :int64 |
  :unsigned_int32 |
  :unsigned_int64
options:
  :allowed_sign => '+' | '-' | '' | '+-' (default '+-')
  :allowed_signs => (same as :allowed_sign)
  :base => integer only (default 10)

@ example

p = prim :double
assert_equal 1.23, p.parse('1.23')
p = prim :double, allowed_sign: '-'
assert_equal 1.23, p.parse('1.23')
assert_equal -1.23, p.parse('-1.23')
assert_equal Rsec::INVALID, p.parse('+1.23')
p = prim :int32, base: 36
assert_equal 49713, p.parse('12cx')


84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/rsec/helpers.rb', line 84

def prim type, options={}, &p
  base = options[:base]
  if [:double, :hex_double].index base
    raise 'Floating points does not allow :base'
  end
  base ||= 10
  Rsec.assert_type base, Integer
  unless (2..36).include? base
    raise RangeError, ":base should be in 2..36, but got #{base}"
  end
  
  sign_strategy = \
    case (options[:allowed_sign] or options[:allowed_signs])
    when nil, '+-', '-+'; 3
    when '+'; 2
    when '-'; 1
    when ''; 0
    else raise "allowed_sign should be one of nil, '', '+', '-', '+-', '-+'"
    end

  parser = \
    case type
    when :double; PDouble.new sign_strategy, false # decimal
    when :hex_double; raise "Removed because Ruby 1.9.3 removed float from hex" # PDouble.new sign_strategy, true # hex
    when :int32;  PInt32.new sign_strategy, base
    when :int64;  PInt64.new sign_strategy, base
    when :unsigned_int32;
      raise 'unsigned int not allow - sign' if options[:allowed_signs] =~ /-/
      PUnsignedInt32.new sign_strategy, base
    when :unsigned_int64;
      raise 'unsigned int not allow - sign' if options[:allowed_signs] =~ /-/
      PUnsignedInt64.new sign_strategy, base
    else
      raise "Invalid primitive type #{type}"
    end
  parser.map p
end

#seq(*xs, &p) ⇒ Object

@ desc.helper

Sequence parser

@ example

assert_equal ['a', 'b', 'c'], actualseq('a', 'b', 'c').parse('abc')


126
127
128
129
# File 'lib/rsec/helpers.rb', line 126

def seq *xs, &p
  xs.map! {|x| Rsec.make_parser x }
  Seq[xs].map p
end

#seq_(*xs, &p) ⇒ Object

@ desc.helper

Sequence parser with skippable pattern(or parser)
option
  :skip default= /\s*/

@ example

assert_equal ['a', 'b', 'c'], actualseq_('a', 'b', 'c', skip: ',').parse('a,b,c')


137
138
139
140
141
142
143
144
145
146
147
# File 'lib/rsec/helpers.rb', line 137

def seq_ *xs, &p
  skipper = 
    if (xs.last.is_a? Hash)
      xs.pop[:skip]
    end
  skipper = skipper ? Rsec.make_parser(skipper) : /\s*/.r
  xs.map! {|x| Rsec.make_parser x }
  first, *rest = xs
  raise 'sequence should not be empty' unless first
  Seq_[first, rest, skipper].map p
end

#symbol(pattern, skip = /\s*/, &p) ⇒ Object

@ desc.helper

A symbol is something wrapped with optional space


151
152
153
154
155
# File 'lib/rsec/helpers.rb', line 151

def symbol pattern, skip=/\s*/, &p
  pattern = Rsec.make_parser pattern
  skip = Rsec.try_skip_pattern Rsec.make_parser skip
  SeqOne[[skip, pattern, skip], 1].map p
end

#word(pattern, &p) ⇒ Object

@ desc.helper

A word is wrapped with word boundaries

@ example

assert_equal ['yes', '3'], seq('yes', '3').parse('yes3')
assert_equal INVALID, seq(word('yes'), '3').parse('yes3')


162
163
164
165
166
# File 'lib/rsec/helpers.rb', line 162

def word pattern, &p
  parser = Rsec.make_parser pattern
  # TODO check pattern type
  Pattern[/\b#{parser.some}\b/].map p
end