Skip to content

Commit a535c5b

Browse files
committed
♻️ Update response to use new parser style
* Fix `accept_spaces?` and use it from `response`. * Add `lookahead!(*tokens)`, an unconsuming version of `match`. The main benefit is that it produces more helpful errors. * Rename `response_untagged` => `response_data`, to match the RFC ABNF
1 parent 3017877 commit a535c5b

File tree

2 files changed

+35
-20
lines changed

2 files changed

+35
-20
lines changed

lib/net/imap/response_parser.rb

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ def unescape_quoted(quoted)
326326
# TODO: add to lexer and only match tagged-ext-label
327327
def_token_matchers :tagged_ext_label, T_ATOM, T_NIL, send: :upcase
328328

329+
def_token_matchers :CRLF, T_CRLF
330+
def_token_matchers :EOF, T_EOF
331+
329332
# atom = 1*ATOM-CHAR
330333
# ATOM-CHAR = <any CHAR except atom-specials>
331334
ATOM_TOKENS = [T_ATOM, T_NUMBER, T_NIL, T_LBRA, T_PLUS]
@@ -403,23 +406,25 @@ def case_insensitive__nstring
403406
alias number64 number
404407
alias number64? number?
405408

409+
# [RFC3501 & RFC9051:]
410+
# response = *(continue-req / response-data) response-done
411+
#
412+
# For simplicity, response isn't interpreted as the combination of the
413+
# three response types, but instead represents any individual server
414+
# response. Our simplified interpretation is defined as:
415+
# response = continue-req | response_data | response-tagged
416+
#
417+
# n.b: our "response-tagged" definition parses "greeting" too.
406418
def response
407-
token = lookahead
408-
case token.symbol
409-
when T_PLUS
410-
result = continue_req
411-
when T_STAR
412-
result = response_untagged
413-
else
414-
result = response_tagged
415-
end
416-
while lookahead.symbol == T_SPACE
417-
# Ignore trailing space for Microsoft Exchange Server
418-
shift_token
419-
end
420-
match(T_CRLF)
421-
match(T_EOF)
422-
return result
419+
resp = case lookahead!(T_PLUS, T_STAR, *TAG_TOKENS).symbol
420+
when T_PLUS then continue_req
421+
when T_STAR then response_data
422+
else response_tagged
423+
end
424+
accept_spaces # QUIRKY: Ignore trailing space (MS Exchange Server?)
425+
CRLF!
426+
EOF!
427+
resp
423428
end
424429

425430
# RFC3501 & RFC9051:
@@ -434,7 +439,7 @@ def continue_req
434439
ContinuationRequest.new(SP? ? resp_text : ResponseText::EMPTY, @str)
435440
end
436441

437-
def response_untagged
442+
def response_data
438443
match(T_STAR)
439444
match(T_SPACE)
440445
token = lookahead
@@ -1566,10 +1571,10 @@ def nil_atom
15661571
#
15671572
# This advances @pos directly so it's safe before changing @lex_state.
15681573
def accept_spaces
1569-
shift_token if @token&.symbol == T_SPACE
1570-
if @str.index(SPACES_REGEXP, @pos)
1574+
return false unless SP?
1575+
@str.index(SPACES_REGEXP, @pos) and
15711576
@pos = $~.end(0)
1572-
end
1577+
true
15731578
end
15741579

15751580
def next_token

lib/net/imap/response_parser/parser_utils.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,16 @@ def lookahead
170170
@token ||= next_token
171171
end
172172

173+
# like match, without consuming the token
174+
def lookahead!(*args)
175+
if args.include?((@token ||= next_token)&.symbol)
176+
@token
177+
else
178+
parse_error('unexpected token %s (expected %s)',
179+
@token&.symbol, args.join(" or "))
180+
end
181+
end
182+
173183
def peek_str?(str)
174184
assert_no_lookahead if Net::IMAP.debug
175185
@str[@pos, str.length] == str

0 commit comments

Comments
 (0)