1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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 78 79 80 81 82 83 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
module Rsec #:nodoc
# transform parse result
class Map < Binary
def _parse ctx
res = left()._parse ctx
return INVALID if INVALID[res]
right()[res]
end
end
# set expect tokens for parsing error in ctx<br/>
# if left failed, the error would be registered
class Fail < Binary
def Fail.[] left, tokens
# TODO mutex
if @mask_bit > 1000
raise "You've created too many fail parsers, If it is your intention, call Rsec::Fail.reset when previous expect settings can be thrown away."
end
parser = super(left, (1<<@mask_bit))
@token_table[@mask_bit] = tokens
@mask_bit += 1
parser
end
def Fail.reset
@mask_bit = 0
@token_table = []
end
Fail.reset
def Fail.get_tokens mask
res = []
@token_table.each_with_index do |tokens, idx|
next unless tokens
if (mask & (1<<idx)) > 0
res += tokens
end
end
res.uniq!
res
end
def _parse ctx
res = left()._parse ctx
ctx.on_fail right if INVALID[res]
res
end
end
# look ahead
class LookAhead < Binary
def _parse ctx
res = left()._parse ctx
pos = ctx.pos
return INVALID if INVALID[right()._parse ctx]
ctx.pos = pos
res
end
end
# negative look ahead
class NegativeLookAhead < Binary
def _parse ctx
res = left()._parse ctx
pos = ctx.pos
return INVALID unless INVALID[right()._parse ctx]
ctx.pos = pos
res
end
end
# branch combinator<br/>
# result in one of the members, or INVALID
class Branch < Unary
def _parse ctx
save_point = ctx.pos
some.each do |e|
res = e._parse ctx
return res unless INVALID[res]
ctx.pos = save_point
end
INVALID
end
end
# matches a pattern
class Pattern < Unary
def _parse ctx
ctx.scan some() or INVALID
end
end
# scan until the pattern<br/>
# for optimizing
class UntilPattern < Unary
def _parse ctx
ctx.scan_until some() or INVALID
end
end
# for optimization, not disposed to users
class SkipPattern < Unary
def _parse ctx
ctx.skip some() or INVALID
end
end
# for optimization, not disposed to users
class SkipUntilPattern < Unary
def _parse ctx
ctx.skip_until some() or INVALID
end
end
# should be end-of-file after parsing
# FIXME seems parser keeps a state when using parse!, see nasm manual parse
class Eof < Unary
def _parse ctx
ret = some()._parse ctx
ctx.eos? ? ret : INVALID
end
end
# one of char in string
class OneOf < Unary
def _parse ctx
return INVALID if ctx.eos?
chr = ctx.getch
if some().index(chr)
chr
else
ctx.pos = ctx.pos - 1
INVALID
end
end
end
# one of char in string
class OneOf_ < Unary
def _parse ctx
ctx.skip /\s*/
return INVALID if ctx.eos?
chr = ctx.getch
unless some().index(chr)
return INVALID
end
ctx.skip /\s*/
chr
end
end
# sometimes a variable is not defined yet<br/>
# lazy is used to capture it later
# NOTE the value is captured the first time it is called
class Lazy < Unary
def _parse ctx
@some ||= \
begin
some()[]
rescue NameError => ex
some().binding.eval ex.name.to_s
end
@some._parse ctx
end
end
# parse result is cached in ctx.
# may improve performance
class Cached
include Parser
def self.[] parser
self.new parser
end
def initialize parser
@parser = parser
@salt = object_id() << 32
end
def _parse ctx
key = ctx.pos | @salt
cache = ctx.cache
# result maybe nil, so don't use ||=
if cache.has_key? key
ret, pos = cache[key]
ctx.pos = pos
ret
else
ret = @parser._parse ctx
pos = ctx.pos
cache[key] = [ret, pos]
ret
end
end
end
end
|