module Net::IMAP::ResponseParser::ParserUtils
def accept(*args)
returns and shifts token on successful match
like match, but does not raise error on failure.
def accept(*args) token = lookahead if args.include?(token.symbol) shift_token token end end
def accept_re(re)
def accept_re(re) assert_no_lookahead if config.debug? re.match(@str, @pos) and @pos = $~.end(0) $~ end
def assert_no_lookahead
To be used conditionally:
def assert_no_lookahead @token.nil? or parse_error("assertion failed: expected @token.nil?, actual %s: %p", @token.symbol, @token.value) end
def combine_adjacent(*tokens)
TODO: after checking the lookahead, use a regexp for remaining chars.
def combine_adjacent(*tokens) result = "".b while token = accept(*tokens) result << token.value end if result.empty? parse_error('unexpected token %s (expected %s)', lookahead.symbol, tokens.join(" or ")) end result end
def lookahead
def lookahead @token ||= next_token end
def lookahead!(*args)
def lookahead!(*args) if args.include?((@token ||= next_token)&.symbol) @token else parse_error('unexpected token %s (expected %s)', @token&.symbol, args.join(" or ")) end end
def lookahead?(*symbols)
def lookahead?(*symbols) @token if symbols.include?((@token ||= next_token)&.symbol) end
def match(*args)
def match(*args) token = lookahead unless args.include?(token.symbol) parse_error('unexpected token %s (expected %s)', token.symbol.id2name, args.collect {|i| i.id2name}.join(" or ")) end shift_token token end
def match_re(re, name)
def match_re(re, name) assert_no_lookahead if config.debug? if re.match(@str, @pos) @pos = $~.end(0) $~ else parse_error("invalid #{name}") end end
def parse_error(fmt, *args)
def parse_error(fmt, *args) msg = format(fmt, *args) if config.debug? local_path = File.dirname(__dir__) tok = @token ? "%s: %p" % [@token.symbol, @token.value] : "nil" warn "%s %s: %s" % [self.class, __method__, msg] warn " tokenized : %s" % [@str[...@pos].dump] warn " remaining : %s" % [@str[@pos..].dump] warn " @lex_state: %s" % [@lex_state] warn " @pos : %d" % [@pos] warn " @token : %s" % [tok] caller_locations(1..20).each_with_index do |cloc, idx| next unless cloc.path&.start_with?(local_path) warn " caller[%2d]: %-30s (%s:%d)" % [ idx, cloc.base_label, File.basename(cloc.path, ".rb"), cloc.lineno ] end end raise ResponseParseError, msg end
def peek_re(re)
def peek_re(re) assert_no_lookahead if config.debug? re.match(@str, @pos) end
def peek_re?(re)
def peek_re?(re) assert_no_lookahead if Net::IMAP.debug re.match?(@str, @pos) end
def peek_str?(str)
def peek_str?(str) assert_no_lookahead if config.debug? @str[@pos, str.length] == str end
def shift_token
def shift_token @token = nil end