]>
git.proxmox.com Git - rustc.git/blob - src/libsyntax_ext/format_foreign.rs
2 use super::strcursor
::StrCursor
as Cur
;
3 use syntax_pos
::InnerSpan
;
5 /// Represents a single `printf`-style substitution.
6 #[derive(Clone, PartialEq, Debug)]
7 pub enum Substitution
<'a
> {
8 /// A formatted output substitution with its internal byte offset.
10 /// A literal `%%` escape.
14 impl<'a
> Substitution
<'a
> {
15 pub fn as_str(&self) -> &str {
17 Substitution
::Format(ref fmt
) => fmt
.span
,
18 Substitution
::Escape
=> "%%",
22 pub fn position(&self) -> Option
<InnerSpan
> {
24 Substitution
::Format(ref fmt
) => Some(fmt
.position
),
29 pub fn set_position(&mut self, start
: usize, end
: usize) {
31 Substitution
::Format(ref mut fmt
) => {
32 fmt
.position
= InnerSpan
::new(start
, end
);
39 /// Translate this substitution into an equivalent Rust formatting directive.
41 /// This ignores cases where the substitution does not have an exact equivalent, or where
42 /// the substitution would be unnecessary.
43 pub fn translate(&self) -> Option
<String
> {
45 Substitution
::Format(ref fmt
) => fmt
.translate(),
46 Substitution
::Escape
=> None
,
51 #[derive(Clone, PartialEq, Debug)]
52 /// A single `printf`-style formatting directive.
53 pub struct Format
<'a
> {
54 /// The entire original formatting directive.
56 /// The (1-based) parameter to be converted.
57 pub parameter
: Option
<u16>,
60 /// Minimum width of the output.
61 pub width
: Option
<Num
>,
62 /// Precision of the conversion.
63 pub precision
: Option
<Num
>,
64 /// Length modifier for the conversion.
65 pub length
: Option
<&'a
str>,
66 /// Type of parameter being converted.
68 /// Byte offset for the start and end of this formatting directive.
69 pub position
: InnerSpan
,
73 /// Translate this directive into an equivalent Rust formatting directive.
75 /// Returns `None` in cases where the `printf` directive does not have an exact Rust
76 /// equivalent, rather than guessing.
77 pub fn translate(&self) -> Option
<String
> {
80 let (c_alt
, c_zero
, c_left
, c_plus
) = {
81 let mut c_alt
= false;
82 let mut c_zero
= false;
83 let mut c_left
= false;
84 let mut c_plus
= false;
85 for c
in self.flags
.chars() {
94 (c_alt
, c_zero
, c_left
, c_plus
)
97 // Has a special form in Rust for numbers.
98 let fill
= c_zero
.then_some("0");
100 let align
= c_left
.then_some("<");
102 // Rust doesn't have an equivalent to the `' '` flag.
103 let sign
= c_plus
.then_some("+");
105 // Not *quite* the same, depending on the type...
108 let width
= match self.width
{
110 // NOTE: Rust doesn't support this.
113 w @
Some(Num
::Arg(_
)) => w
,
114 w @
Some(Num
::Num(_
)) => w
,
118 let precision
= self.precision
;
120 // NOTE: although length *can* have an effect, we can't duplicate the effect in Rust, so
121 // we just ignore it.
123 let (type_
, use_zero_fill
, is_int
) = match self.type_
{
124 "d" | "i" | "u" => (None
, true, true),
125 "f" | "F" => (None
, false, false),
126 "s" | "c" => (None
, false, false),
127 "e" | "E" => (Some(self.type_
), true, false),
128 "x" | "X" | "o" => (Some(self.type_
), true, true),
129 "p" => (Some(self.type_
), false, true),
130 "g" => (Some("e"), true, false),
131 "G" => (Some("E"), true, false),
135 let (fill
, width
, precision
) = match (is_int
, width
, precision
) {
136 (true, Some(_
), Some(_
)) => {
137 // Rust can't duplicate this insanity.
140 (true, None
, Some(p
)) => (Some("0"), Some(p
), None
),
141 (true, w
, None
) => (fill
, w
, None
),
142 (false, w
, p
) => (fill
, w
, p
),
145 let align
= match (self.type_
, width
.is_some(), align
.is_some()) {
146 ("s", true, false) => Some(">"),
150 let (fill
, zero_fill
) = match (fill
, use_zero_fill
) {
151 (Some("0"), true) => (None
, true),
152 (fill
, _
) => (fill
, false),
155 let alt
= match type_
{
156 Some("x") | Some("X") => alt
,
160 let has_options
= fill
.is_some()
166 || precision
.is_some()
170 // Initialise with a rough guess.
171 let cap
= self.span
.len() + if has_options { 2 }
else { 0 }
;
172 let mut s
= String
::with_capacity(cap
);
176 if let Some(arg
) = self.parameter
{
177 write
!(s
, "{}", arg
.checked_sub(1)?
).ok()?
;
183 let align
= if let Some(fill
) = fill
{
190 if let Some(align
) = align
{
194 if let Some(sign
) = sign
{
206 if let Some(width
) = width
{
207 width
.translate(&mut s
).ok()?
;
210 if let Some(precision
) = precision
{
212 precision
.translate(&mut s
).ok()?
;
215 if let Some(type_
) = type_
{
225 /// A general number used in a `printf` formatting directive.
226 #[derive(Copy, Clone, PartialEq, Debug)]
228 // The range of these values is technically bounded by `NL_ARGMAX`... but, at least for GNU
229 // libc, it apparently has no real fixed limit. A `u16` is used here on the basis that it
230 // is *vanishingly* unlikely that *anyone* is going to try formatting something wider, or
231 // with more precision, than 32 thousand positions which is so wide it couldn't possibly fit
234 /// A specific, fixed value.
236 /// The value is derived from a positional argument.
238 /// The value is derived from the "next" unconverted argument.
243 fn from_str(s
: &str, arg
: Option
<&str>) -> Self {
244 if let Some(arg
) = arg
{
245 Num
::Arg(arg
.parse().unwrap_or_else(|_
| panic
!("invalid format arg `{:?}`", arg
)))
249 Num
::Num(s
.parse().unwrap_or_else(|_
| panic
!("invalid format num `{:?}`", s
)))
253 fn translate(&self, s
: &mut String
) -> std
::fmt
::Result
{
256 Num
::Num(n
) => write
!(s
, "{}", n
),
258 let n
= n
.checked_sub(1).ok_or(std
::fmt
::Error
)?
;
261 Num
::Next
=> write
!(s
, "*"),
266 /// Returns an iterator over all substitutions in a given string.
267 pub fn iter_subs(s
: &str, start_pos
: usize) -> Substitutions
<'_
> {
274 /// Iterator over substitutions in a string.
275 pub struct Substitutions
<'a
> {
280 impl<'a
> Iterator
for Substitutions
<'a
> {
281 type Item
= Substitution
<'a
>;
282 fn next(&mut self) -> Option
<Self::Item
> {
283 let (mut sub
, tail
) = parse_next_substitution(self.s
)?
;
286 Substitution
::Format(_
) => if let Some(inner_span
) = sub
.position() {
287 sub
.set_position(inner_span
.start
+ self.pos
, inner_span
.end
+ self.pos
);
288 self.pos
+= inner_span
.end
;
290 Substitution
::Escape
=> self.pos
+= 2,
295 fn size_hint(&self) -> (usize, Option
<usize>) {
296 // Substitutions are at least 2 characters long.
297 (0, Some(self.s
.len() / 2))
312 /// Parse the next substitution from the input string.
313 pub fn parse_next_substitution(s
: &str) -> Option
<(Substitution
<'_
>, &str)> {
317 let start
= s
.find('
%'
)?
;
318 match s
[start
+1..].chars().next()?
{
319 '
%'
=> return Some((Substitution
::Escape
, &s
[start
+2..])),
320 _
=> {/* fall-through */}
,
323 Cur
::new_at(&s
[..], start
)
326 // This is meant to be a translation of the following regex:
331 // (?: (?P<parameter> \d+) \$ )?
332 // (?P<flags> [-+ 0\#']* )
333 // (?P<width> \d+ | \* (?: (?P<widtha> \d+) \$ )? )?
334 // (?: \. (?P<precision> \d+ | \* (?: (?P<precisiona> \d+) \$ )? ) )?
337 // hh | h | ll | l | L | z | j | t
340 // | I32 | I64 | I | q
345 // Used to establish the full span at the end.
347 // The current position within the string.
348 let mut at
= at
.at_next_cp()?
;
349 // `c` is the next codepoint, `next` is a cursor after it.
350 let (mut c
, mut next
) = at
.next_cp()?
;
352 // Update `at`, `c`, and `next`, exiting if we're out of input.
353 macro_rules
! move_to
{
357 let (c_
, next_
) = at
.next_cp()?
;
364 // Constructs a result when parsing fails.
366 // Note: `move` used to capture copies of the cursors as they are *now*.
367 let fallback
= move || {
369 Substitution
::Format(Format
{
370 span
: start
.slice_between(next
).unwrap(),
376 type_
: at
.slice_between(next
).unwrap(),
377 position
: InnerSpan
::new(start
.at
, next
.at
),
383 // Next parsing state.
384 let mut state
= Start
;
386 // Sadly, Rust isn't *quite* smart enough to know these *must* be initialised by the end.
387 let mut parameter
: Option
<u16> = None
;
388 let mut flags
: &str = "";
389 let mut width
: Option
<Num
> = None
;
390 let mut precision
: Option
<Num
> = None
;
391 let mut length
: Option
<&str> = None
;
392 let mut type_
: &str = "";
395 if let Start
= state
{
398 let end
= at_next_cp_while(next
, is_digit
);
399 match end
.next_cp() {
400 // Yes, this *is* the parameter.
401 Some(('$'
, end2
)) => {
403 parameter
= Some(at
.slice_between(end
).unwrap().parse().unwrap());
406 // Wait, no, actually, it's the width.
411 width
= Some(Num
::from_str(at
.slice_between(end
).unwrap(), None
));
414 // It's invalid, is what it is.
415 None
=> return fallback(),
426 if let Flags
= state
{
427 let end
= at_next_cp_while(at
, is_flag
);
429 flags
= at
.slice_between(end
).unwrap();
433 if let Width
= state
{
440 let end
= at_next_cp_while(next
, is_digit
);
442 width
= Some(Num
::from_str(at
.slice_between(end
).unwrap(), None
));
453 if let WidthArg
= state
{
454 let end
= at_next_cp_while(at
, is_digit
);
455 match end
.next_cp() {
456 Some(('$'
, end2
)) => {
458 width
= Some(Num
::from_str("", Some(at
.slice_between(end
).unwrap())));
463 width
= Some(Num
::Next
);
469 if let Prec
= state
{
483 if let PrecInner
= state
{
486 let end
= at_next_cp_while(next
, is_digit
);
487 match end
.next_cp() {
488 Some(('$'
, end2
)) => {
490 precision
= Some(Num
::from_str("*", next
.slice_between(end
)));
495 precision
= Some(Num
::Next
);
501 let end
= at_next_cp_while(next
, is_digit
);
503 precision
= Some(Num
::from_str(at
.slice_between(end
).unwrap(), None
));
506 _
=> return fallback(),
510 if let Length
= state
{
511 let c1_next1
= next
.next_cp();
512 match (c
, c1_next1
) {
513 ('h'
, Some(('h'
, next1
)))
514 | ('l'
, Some(('l'
, next1
)))
517 length
= Some(at
.slice_between(next1
).unwrap());
521 ('h'
, _
) | ('l'
, _
) | ('L'
, _
)
522 | ('z'
, _
) | ('j'
, _
) | ('t'
, _
)
526 length
= Some(at
.slice_between(next
).unwrap());
531 let end
= next
.at_next_cp()
532 .and_then(|end
| end
.at_next_cp())
533 .map(|end
| (next
.slice_between(end
).unwrap(), end
));
534 let end
= match end
{
535 Some(("32", end
)) => end
,
536 Some(("64", end
)) => end
,
540 length
= Some(at
.slice_between(end
).unwrap());
552 if let Type
= state
{
554 type_
= at
.slice_between(next
).unwrap();
556 // Don't use `move_to!` here, as we *can* be at the end of the input.
564 let position
= InnerSpan
::new(start
.at
, end
.at
);
567 span
: start
.slice_between(end
).unwrap(),
576 Some((Substitution
::Format(f
), end
.slice_after()))
579 fn at_next_cp_while
<F
>(mut cur
: Cur
<'_
>, mut pred
: F
) -> Cur
<'_
>
580 where F
: FnMut(char) -> bool
{
582 match cur
.next_cp() {
583 Some((c
, next
)) => if pred(c
) {
593 fn is_digit(c
: char) -> bool
{
600 fn is_flag(c
: char) -> bool
{
602 '
0'
| '
-'
| '
+'
| ' '
| '
#' | '\'' => true,
612 use super::strcursor
::StrCursor
as Cur
;
613 use syntax_pos
::InnerSpan
;
615 #[derive(Clone, PartialEq, Debug)]
616 pub enum Substitution
<'a
> {
617 Ordinal(u8, (usize, usize)),
618 Name(&'a
str, (usize, usize)),
619 Escape((usize, usize)),
622 impl Substitution
<'_
> {
623 pub fn as_str(&self) -> String
{
625 Substitution
::Ordinal(n
, _
) => format
!("${}", n
),
626 Substitution
::Name(n
, _
) => format
!("${}", n
),
627 Substitution
::Escape(_
) => "$$".into(),
631 pub fn position(&self) -> Option
<InnerSpan
> {
633 Substitution
::Ordinal(_
, pos
) |
634 Substitution
::Name(_
, pos
) |
635 Substitution
::Escape(pos
) => Some(InnerSpan
::new(pos
.0, pos
.1)),
639 pub fn set_position(&mut self, start
: usize, end
: usize) {
641 Substitution
::Ordinal(_
, ref mut pos
) |
642 Substitution
::Name(_
, ref mut pos
) |
643 Substitution
::Escape(ref mut pos
) => *pos
= (start
, end
),
647 pub fn translate(&self) -> Option
<String
> {
649 Substitution
::Ordinal(n
, _
) => Some(format
!("{{{}}}", n
)),
650 Substitution
::Name(n
, _
) => Some(format
!("{{{}}}", n
)),
651 Substitution
::Escape(_
) => None
,
656 /// Returns an iterator over all substitutions in a given string.
657 pub fn iter_subs(s
: &str, start_pos
: usize) -> Substitutions
<'_
> {
664 /// Iterator over substitutions in a string.
665 pub struct Substitutions
<'a
> {
670 impl<'a
> Iterator
for Substitutions
<'a
> {
671 type Item
= Substitution
<'a
>;
672 fn next(&mut self) -> Option
<Self::Item
> {
673 match parse_next_substitution(self.s
) {
674 Some((mut sub
, tail
)) => {
676 if let Some(InnerSpan { start, end }
) = sub
.position() {
677 sub
.set_position(start
+ self.pos
, end
+ self.pos
);
686 fn size_hint(&self) -> (usize, Option
<usize>) {
687 (0, Some(self.s
.len()))
691 /// Parse the next substitution from the input string.
692 pub fn parse_next_substitution(s
: &str) -> Option
<(Substitution
<'_
>, &str)> {
694 let start
= s
.find('$'
)?
;
695 match s
[start
+1..].chars().next()?
{
696 '$'
=> return Some((Substitution
::Escape((start
, start
+2)), &s
[start
+2..])),
698 let n
= (c
as u8) - b'
0'
;
699 return Some((Substitution
::Ordinal(n
, (start
, start
+2)), &s
[start
+2..]));
701 _
=> {/* fall-through */}
,
704 Cur
::new_at(&s
[..], start
)
707 let at
= at
.at_next_cp()?
;
708 let (c
, inner
) = at
.next_cp()?
;
710 if !is_ident_head(c
) {
713 let end
= at_next_cp_while(inner
, is_ident_tail
);
714 let slice
= at
.slice_between(end
).unwrap();
715 let start
= at
.at
- 1;
716 let end_pos
= at
.at
+ slice
.len();
717 Some((Substitution
::Name(slice
, (start
, end_pos
)), end
.slice_after()))
721 fn at_next_cp_while
<F
>(mut cur
: Cur
<'_
>, mut pred
: F
) -> Cur
<'_
>
722 where F
: FnMut(char) -> bool
{
724 match cur
.next_cp() {
725 Some((c
, next
)) => if pred(c
) {
735 fn is_ident_head(c
: char) -> bool
{
737 'a'
..= 'z'
| 'A'
..= 'Z'
| '_'
=> true,
742 fn is_ident_tail(c
: char) -> bool
{
745 c
=> is_ident_head(c
)
754 pub struct StrCursor
<'a
> {
759 impl<'a
> StrCursor
<'a
> {
760 pub fn new_at(s
: &'a
str, at
: usize) -> StrCursor
<'a
> {
767 pub fn at_next_cp(mut self) -> Option
<StrCursor
<'a
>> {
768 match self.try_seek_right_cp() {
774 pub fn next_cp(mut self) -> Option
<(char, StrCursor
<'a
>)> {
775 let cp
= self.cp_after()?
;
776 self.seek_right(cp
.len_utf8());
780 fn slice_before(&self) -> &'a
str {
784 pub fn slice_after(&self) -> &'a
str {
788 pub fn slice_between(&self, until
: StrCursor
<'a
>) -> Option
<&'a
str> {
789 if !str_eq_literal(self.s
, until
.s
) {
792 use std
::cmp
::{max, min}
;
793 let beg
= min(self.at
, until
.at
);
794 let end
= max(self.at
, until
.at
);
795 Some(&self.s
[beg
..end
])
799 fn cp_after(&self) -> Option
<char> {
800 self.slice_after().chars().next()
803 fn try_seek_right_cp(&mut self) -> bool
{
804 match self.slice_after().chars().next() {
806 self.at
+= c
.len_utf8();
813 fn seek_right(&mut self, bytes
: usize) {
818 impl Copy
for StrCursor
<'_
> {}
820 impl<'a
> Clone
for StrCursor
<'a
> {
821 fn clone(&self) -> StrCursor
<'a
> {
826 impl std
::fmt
::Debug
for StrCursor
<'_
> {
827 fn fmt(&self, fmt
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
828 write
!(fmt
, "StrCursor({:?} | {:?})", self.slice_before(), self.slice_after())
832 fn str_eq_literal(a
: &str, b
: &str) -> bool
{
833 a
.as_bytes().as_ptr() == b
.as_bytes().as_ptr()
834 && a
.len() == b
.len()