diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index e437c32e347..c0df49a7fee 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -100,6 +100,7 @@ GRS_OBJS = \
rust/rust-proc-macro-invoc-lexer.o \
rust/rust-macro-substitute-ctx.o \
rust/rust-macro-builtins.o \
+ rust/rust-fmt.o \
rust/rust-hir.o \
rust/rust-hir-map.o \
rust/rust-attributes.o \
diff --git a/gcc/rust/ast/rust-fmt.cc b/gcc/rust/ast/rust-fmt.cc
new file mode 100644
index 00000000000..9f9ba48f0c3
--- /dev/null
+++ b/gcc/rust/ast/rust-fmt.cc
@@ -0,0 +1,96 @@
+// Copyright (C) 2020-2023 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#include "rust-fmt.h"
+
+namespace Rust {
+tl::expected
+Fmt::parse_fmt_string (Fmt::Input input)
+{
+ return Fmt ();
+}
+
+tl::expected>, Fmt::Error>
+Fmt::maybe_format (Fmt::Input input)
+{
+ tl::optional none = tl::nullopt;
+
+ return Fmt::Result (input, none);
+}
+
+tl::expected, Fmt::Error>
+Fmt::format (Input input)
+{
+ return Fmt::Result (input, Format ());
+}
+
+tl::expected, Fmt::Error>
+Fmt::argument (Input input)
+{
+ return Fmt::Result (input, Argument ());
+}
+
+tl::expected, Fmt::Error>
+Fmt::format_spec (Input input)
+{
+ return Fmt::Result (input, FormatSpec ());
+}
+
+tl::expected, Fmt::Error>
+Fmt::fill (Input input)
+{
+ return Fmt::Result (input, Fill ());
+}
+
+tl::expected, Fmt::Error>
+Fmt::align (Input input)
+{
+ switch (input[0])
+ {
+ case '<':
+ return Fmt::Result (input.substr (1), Align::Left);
+ case '^':
+ return Fmt::Result (input.substr (1), Align::Top);
+ case '>':
+ return Fmt::Result (input.substr (1), Align::Right);
+ default:
+ // TODO: Store the character here
+ // TODO: Can we have proper error locations?
+ // TODO: Maybe we should use a Rust::Literal string instead of a string
+ return tl::make_unexpected (Error::Align);
+ }
+}
+
+tl::expected, Fmt::Error>
+Fmt::sign (Input input)
+{
+ switch (input[0])
+ {
+ case '+':
+ return Fmt::Result (input.substr (1), Sign::Plus);
+ case '-':
+ return Fmt::Result (input.substr (1), Sign::Minus);
+ default:
+ // TODO: Store the character here
+ // TODO: Can we have proper error locations?
+ // TODO: Maybe we should use a Rust::Literal string instead of a string
+ return tl::make_unexpected (Error::Sign);
+ }
+}
+
+} // namespace Rust
diff --git a/gcc/rust/ast/rust-fmt.h b/gcc/rust/ast/rust-fmt.h
new file mode 100644
index 00000000000..f3dd53da979
--- /dev/null
+++ b/gcc/rust/ast/rust-fmt.h
@@ -0,0 +1,133 @@
+// Copyright (C) 2020-2023 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#ifndef RUST_FMT_H
+#define RUST_FMT_H
+
+#include "expected.h"
+#include "optional.h"
+#include "rust-ast.h"
+#include "rust-system.h"
+
+namespace Rust {
+
+/**
+ * This class implements the parsing of Rust format strings according to the
+ * grammar here: https://doc.rust-lang.org/std/fmt/index.html#syntax
+ */
+// TODO: Are there features that are only present in specific Rust editions?
+class Fmt
+{
+public:
+ // TODO: Keep location information
+ // TODO: Switch to a Rust::AST::Literal here
+ using Input = std::string;
+
+ enum class Error
+ {
+ Align,
+ Sign,
+ };
+
+ template class Result
+ {
+ public:
+ explicit Result (Input remaining_input, T result)
+ : remaining_input (remaining_input), result (result)
+ {}
+
+ private:
+ Input remaining_input;
+ T result;
+ };
+
+ // FIXME: Do not use an owned string here
+ static tl::expected parse_fmt_string (Input input);
+
+private:
+ // the parse functions should return the remaining input as well as the
+ // expected node let's look at nom
+ // TODO: no string view :( use an owned string for now?
+
+ template struct ParseResult
+ {
+ tl::expected, Error> inner;
+
+ ParseResult (tl::expected, Error> inner) : inner (inner) {}
+ ParseResult operator= (tl::expected, Error> inner)
+ {
+ return ParseResult (inner);
+ }
+
+ Input remaining_input () { return inner->remaining_input; }
+ T value () { return inner->value; }
+ };
+
+ struct Format
+ {
+ };
+
+ struct Argument
+ {
+ enum struct Kind
+ {
+ Integer,
+ Identifier,
+ } kind;
+
+ int integer;
+ Identifier identifier;
+ };
+
+ struct FormatSpec
+ {
+ };
+
+ struct Fill
+ {
+ char to_fill;
+ };
+
+ enum class Align
+ {
+ Left,
+ Top,
+ Right
+ };
+
+ enum class Sign
+ {
+ Plus,
+ Minus
+ };
+
+ // let's do one function per rule in the BNF
+ static tl::expected, Error> text (Input input);
+ static tl::expected>, Error>
+ maybe_format (Input input);
+ static tl::expected, Error> format (Input input);
+ static tl::expected, Error> argument (Input input);
+ static tl::expected, Error> format_spec (Input input);
+ static tl::expected, Error> fill (Input input);
+ static tl::expected, Error> align (Input input);
+ static tl::expected, Error> sign (Input input);
+};
+
+} // namespace Rust
+
+#endif // ! RUST_FMT_H