@@ -427,6 +427,17 @@ def case_insensitive__nstring
427427 alias nz_number number
428428 alias nz_number? number?
429429
430+ # valid number ranges are not enforced by parser
431+ # nz-number64 = digit-nz *DIGIT
432+ # ; Unsigned 63-bit integer
433+ # ; (0 < n <= 9,223,372,036,854,775,807)
434+ alias nz_number64 nz_number
435+
436+ # valid number ranges are not enforced by parser
437+ # uniqueid = nz-number
438+ # ; Strictly ascending
439+ alias uniqueid nz_number
440+
430441 # [RFC3501 & RFC9051:]
431442 # response = *(continue-req / response-data) response-done
432443 #
@@ -658,20 +669,21 @@ def msg_att(n)
658669 lpar
659670 attr = { }
660671 while true
661- name = lookahead! ( T_ATOM ) . value . upcase
662- name , val =
672+ name = msg_att__label ; SP!
673+ val =
663674 case name
664- when "UID" then uid_data
665- when "FLAGS" then flags_data
666- when "BODY" then body_data
667- when "BODYSTRUCTURE" then body_data
668- when "ENVELOPE" then envelope_data
669- when "INTERNALDATE" then internaldate_data
670- when "RFC822.SIZE" then rfc822_size
671- when "RFC822" then rfc822_text
672- when "RFC822.HEADER" then rfc822_text # not in rev2
673- when "RFC822.TEXT" then rfc822_text # not in rev2
674- when "MODSEQ" then modseq_data # CONDSTORE
675+ when "UID" then uniqueid
676+ when "FLAGS" then flag_list
677+ when "BODY" then body
678+ when /\A BODY\[ /ni then nstring
679+ when "BODYSTRUCTURE" then body
680+ when "ENVELOPE" then envelope
681+ when "INTERNALDATE" then date_time
682+ when "RFC822.SIZE" then number64
683+ when "RFC822" then nstring # not in rev2
684+ when "RFC822.HEADER" then nstring # not in rev2
685+ when "RFC822.TEXT" then nstring # not in rev2
686+ when "MODSEQ" then parens__modseq # CONDSTORE
675687 else parse_error ( "unknown attribute `%s' for {%d}" , name , n )
676688 end
677689 attr [ name ] = val
@@ -682,11 +694,17 @@ def msg_att(n)
682694 attr
683695 end
684696
685- def envelope_data
686- token = match ( T_ATOM )
687- name = token . value . upcase
688- match ( T_SPACE )
689- return name , envelope
697+ # appends "[section]" and "<partial>" to the base label
698+ def msg_att__label
699+ case ( name = tagged_ext_label )
700+ when /\A (?:RFC822(?:\. HEADER|\. TEXT)?)\z /ni
701+ # ignoring "[]" fixes https://bugs.ruby-lang.org/issues/5620
702+ lbra? and rbra
703+ when "BODY"
704+ peek_lbra? and name << section and
705+ peek_str? ( "<" ) and name << atom # partial
706+ end
707+ name
690708 end
691709
692710 def envelope
@@ -724,58 +742,10 @@ def envelope
724742 return result
725743 end
726744
727- def flags_data
728- token = match ( T_ATOM )
729- name = token . value . upcase
730- match ( T_SPACE )
731- return name , flag_list
732- end
733-
734- def internaldate_data
735- token = match ( T_ATOM )
736- name = token . value . upcase
737- match ( T_SPACE )
738- token = match ( T_QUOTED )
739- return name , token . value
740- end
741-
742- def rfc822_text
743- token = match ( T_ATOM )
744- name = token . value . upcase
745- token = lookahead
746- if token . symbol == T_LBRA
747- shift_token
748- match ( T_RBRA )
749- end
750- match ( T_SPACE )
751- return name , nstring
752- end
753-
754- def rfc822_size
755- token = match ( T_ATOM )
756- name = token . value . upcase
757- match ( T_SPACE )
758- return name , number
759- end
760-
761- def body_data
762- token = match ( T_ATOM )
763- name = token . value . upcase
764- token = lookahead
765- if token . symbol == T_SPACE
766- shift_token
767- return name , body
768- end
769- name . concat ( section )
770- token = lookahead
771- if token . symbol == T_ATOM
772- name . concat ( token . value )
773- shift_token
774- end
775- match ( T_SPACE )
776- data = nstring
777- return name , data
778- end
745+ # date-time = DQUOTE date-day-fixed "-" date-month "-" date-year
746+ # SP time SP zone DQUOTE
747+ alias date_time quoted
748+ alias ndatetime nquoted
779749
780750 # RFC-3501 & RFC-9051:
781751 # body = "(" (body-type-1part / body-type-mpart) ")"
@@ -1114,23 +1084,6 @@ def header_fld_name
11141084 end
11151085 end
11161086
1117- def uid_data
1118- token = match ( T_ATOM )
1119- name = token . value . upcase
1120- match ( T_SPACE )
1121- return name , number
1122- end
1123-
1124- def modseq_data
1125- token = match ( T_ATOM )
1126- name = token . value . upcase
1127- match ( T_SPACE )
1128- match ( T_LPAR )
1129- modseq = number
1130- match ( T_RPAR )
1131- return name , modseq
1132- end
1133-
11341087 def mailbox_data__flags
11351088 token = match ( T_ATOM )
11361089 name = token . value . upcase
@@ -1698,6 +1651,20 @@ def charset
16981651 end
16991652 end
17001653
1654+ # RFC7162:
1655+ # mod-sequence-value = 1*DIGIT
1656+ # ;; Positive unsigned 63-bit integer
1657+ # ;; (mod-sequence)
1658+ # ;; (1 <= n <= 9,223,372,036,854,775,807).
1659+ alias mod_sequence_value nz_number64
1660+
1661+ # RFC7162:
1662+ # permsg-modsequence = mod-sequence-value
1663+ # ;; Per-message mod-sequence.
1664+ alias permsg_modsequence mod_sequence_value
1665+
1666+ def parens__modseq ; lpar ; _ = permsg_modsequence ; rpar ; _ end
1667+
17011668 # RFC-4315 (UIDPLUS) or RFC9051 (IMAP4rev2):
17021669 # uid-set = (uniqueid / uid-range) *("," uid-set)
17031670 # uid-range = (uniqueid ":" uniqueid)
0 commit comments