#include "rust-ast.h"
#include "optional.h"
+#include "rust-builtin-ast-nodes.h"
#include "rust-system.h"
#include "rust-ast-full.h"
#include "rust-diagnostics.h"
vis.visit (*this);
}
+std::string
+FormatArgs::as_string () const
+{
+ // FIXME(Arthur): Improve
+ return "FormatArgs";
+}
+
+location_t
+FormatArgs::get_locus () const
+{
+ rust_unreachable ();
+}
+
+bool
+FormatArgs::is_expr_without_block () const
+{
+ return false;
+}
+
+void
+FormatArgs::mark_for_strip ()
+{
+ marked_for_strip = true;
+}
+
+bool
+FormatArgs::is_marked_for_strip () const
+{
+ return marked_for_strip;
+}
+
+std::vector<Attribute> &
+FormatArgs::get_outer_attrs ()
+{
+ rust_unreachable ();
+}
+
+void FormatArgs::set_outer_attrs (std::vector<Attribute>)
+{
+ rust_unreachable ();
+}
+
+Expr *
+FormatArgs::clone_expr_impl () const
+{
+ std::cerr << "[ARTHUR] cloning FormatArgs! " << std::endl;
+
+ return new FormatArgs (*this);
+}
+
} // namespace AST
std::ostream &
// └─┘ └─┘
// positions (could be names, numbers, empty, or `*`)
+// FIXME: Merge with the class below this one?
class FormatArgumentKind
{
public:
+ enum class Kind
+ {
+ Normal,
+ Named,
+ Captured,
+ } kind;
+
Identifier &get_ident ()
{
rust_assert (kind == Kind::Captured || kind == Kind::Named);
return ident.value ();
}
-private:
- enum class Kind
+ FormatArgumentKind (Kind kind, tl::optional<Identifier> ident)
+ : kind (kind), ident (ident)
+ {}
+
+ FormatArgumentKind (const FormatArgumentKind &other)
{
- Normal,
- Named,
- Captured,
- } kind;
+ kind = other.kind;
+ ident = other.ident;
+ }
+
+ FormatArgumentKind operator= (const FormatArgumentKind &other)
+ {
+ kind = other.kind;
+ ident = other.ident;
+ return *this;
+ }
+
+private:
tl::optional<Identifier> ident;
};
class FormatArgument
{
+public:
+ static FormatArgument normal (std::unique_ptr<Expr> expr)
+ {
+ return FormatArgument (FormatArgumentKind::Kind::Normal, tl::nullopt,
+ std::move (expr));
+ }
+
+ static FormatArgument named (Identifier ident, std::unique_ptr<Expr> expr)
+ {
+ return FormatArgument (FormatArgumentKind::Kind::Named, ident,
+ std::move (expr));
+ }
+
+ static FormatArgument captured (Identifier ident, std::unique_ptr<Expr> expr)
+ {
+ return FormatArgument (FormatArgumentKind::Kind::Captured, ident,
+ std::move (expr));
+ }
+
+ FormatArgument (const FormatArgument &other)
+ : kind (other.kind), expr (other.expr->clone_expr ())
+ {}
+
+ FormatArgument operator= (const FormatArgument &other)
+ {
+ kind = other.kind;
+ expr = other.expr->clone_expr ();
+
+ return *this;
+ }
+
+private:
+ FormatArgument (FormatArgumentKind::Kind kind, tl::optional<Identifier> ident,
+ std::unique_ptr<Expr> expr)
+ : kind (FormatArgumentKind (kind, ident)), expr (std::move (expr))
+ {}
+
FormatArgumentKind kind;
std::unique_ptr<Expr> expr;
};
class FormatArguments
{
+public:
+ FormatArguments () {}
+ FormatArguments (FormatArguments &&) = default;
+ FormatArguments (const FormatArguments &other)
+ {
+ args = std::vector<FormatArgument> ();
+ args.reserve (other.args.size ());
+
+ for (const auto &arg : other.args)
+ args.emplace_back (arg);
+ };
+
+ FormatArguments &operator= (const FormatArguments &other) = default;
+
+ void push (FormatArgument &&elt) { args.emplace_back (std::move (elt)); }
+
+private:
std::vector<FormatArgument> args;
};
// format_args!("result: {}", some_result))` -> `format_args!("heyo result: {}",
// some_result)`
// FIXME: Move to rust-macro.h
-class FormatArgs : public Visitable
+class FormatArgs : public Expr
{
public:
enum class Newline
No
};
- FormatArgs (location_t loc, Fmt::PieceSlice template_str,
- FormatArguments arguments)
+ FormatArgs (location_t loc, Fmt::Pieces &&template_str,
+ FormatArguments &&arguments)
: loc (loc), template_str (std::move (template_str)),
arguments (std::move (arguments))
{}
- void accept_vis (AST::ASTVisitor &vis);
+ FormatArgs (FormatArgs &&other)
+ : loc (std::move (other.loc)),
+ template_str (std::move (other.template_str)),
+ arguments (std::move (other.arguments))
+ {
+ std::cerr << "[ARTHUR] moving FormatArgs" << std::endl;
+ }
+
+ // FIXME: This might be invalid - we are reusing the same memory allocated
+ // on the Rust side for `other`. This is probably valid as long as we only
+ // ever read that memory and never write to it.
+ FormatArgs (const FormatArgs &other)
+ : loc (other.loc), template_str (other.template_str),
+ arguments (other.arguments)
+ {
+ std::cerr << "[ARTHUR] copying FormatArgs" << std::endl;
+ }
+
+ // FormatArgs &operator= (const FormatArgs &other) = default;
+ // : template_str (other.template_str), arguments (other.arguments)
+ // {}
+
+ void accept_vis (AST::ASTVisitor &vis) override;
private:
location_t loc;
- Fmt::PieceSlice template_str;
+ // FIXME: This probably needs to be a separate type - it is one in rustc's
+ // expansion of format_args!(). There is extra handling associated with it.
+ // we can maybe do that in rust-fmt.cc? in collect_pieces()? like do the
+ // transformation into something we can handle better
+ Fmt::Pieces template_str;
FormatArguments arguments;
+
+ bool marked_for_strip = false;
+
+protected:
+ virtual std::string as_string () const override;
+ virtual location_t get_locus () const override;
+ virtual bool is_expr_without_block () const override;
+ virtual void mark_for_strip () override;
+ virtual bool is_marked_for_strip () const override;
+ virtual std::vector<Attribute> &get_outer_attrs () override;
+ virtual void set_outer_attrs (std::vector<Attribute>) override;
+ virtual Expr *clone_expr_impl () const override;
};
} // namespace AST
namespace Fmt {
Pieces
-Pieces::collect (std::string &&to_parse)
+Pieces::collect (std::string &&to_parse, bool append_newline)
{
- auto piece_slice = collect_pieces (to_parse.c_str ());
+ auto piece_slice = collect_pieces (to_parse.c_str (), append_newline);
rust_debug ("[ARTHUR] %p, %lu", (const void *) piece_slice.base_ptr,
piece_slice.len);
return Pieces (piece_slice, std::move (to_parse));
}
-Pieces::~Pieces () { destroy_pieces (slice); }
+Pieces::~Pieces ()
+{
+ std::cerr << "Arthur: destoying pieces. this: " << (void *) this
+ << " slice: " << slice.base_ptr << std::endl;
+ destroy_pieces (slice);
+}
+
+Pieces::Pieces (const Pieces &other) : to_parse (other.to_parse)
+{
+ slice = clone_pieces (other.slice.base_ptr, other.slice.len, other.slice.cap);
+ std::cerr << "Arthur: copying pieces: other.to_parse: "
+ << (void *) other.to_parse.c_str ()
+ << " ours to_parse: " << (void *) to_parse.c_str () << std::endl;
+ // auto pieces = std::vector (slice.base_ptr, slice.base_ptr + slice.len);
+}
+
+Pieces &
+Pieces::operator= (const Pieces &other)
+{
+ slice = clone_pieces (other.slice.base_ptr, other.slice.len, other.slice.cap);
+ to_parse = other.to_parse;
+
+ return *this;
+}
+
+Pieces::Pieces (Pieces &&other)
+ : slice (
+ clone_pieces (other.slice.base_ptr, other.slice.len, other.slice.cap)),
+ to_parse (std::move (other.to_parse))
+{
+ std::cerr << "Arthur: moving pieces. to_parse: " << (void *) to_parse.c_str ()
+ << " base_ptr/slice: " << (void *) slice.base_ptr << std::endl;
+}
} // namespace Fmt
} // namespace Rust
struct NextArgument_Body
{
- const Argument *_0;
+ Argument _0;
};
Tag tag;
extern "C" {
PieceSlice
-collect_pieces (const char *input);
+collect_pieces (const char *input, bool append_newline);
+
+PieceSlice
+clone_pieces (const Piece *base_ptr, size_t len, size_t cap);
void destroy_pieces (PieceSlice);
struct Pieces
{
- static Pieces collect (std::string &&to_parse);
+ static Pieces collect (std::string &&to_parse, bool append_newline);
~Pieces ();
+ Pieces (const Pieces &other);
+ Pieces &operator= (const Pieces &other);
+
+ Pieces (Pieces &&other);
+
+ // {
+ // slice = clone_pieces (&other.slice);
+ // to_parse = other.to_parse;
+
+ // return *this;
+ // }
+
private:
Pieces (PieceSlice slice, std::string &&to_parse)
: slice (slice), to_parse (std::move (to_parse))
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
+#include "expected.h"
#include "libproc_macro_internal/tokenstream.h"
#include "rust-ast-full-decls.h"
#include "rust-builtin-ast-nodes.h"
#include "rust-session-manager.h"
#include "rust-attribute-values.h"
#include "rust-fmt.h"
+#include "rust-token.h"
namespace Rust {
return AST::Fragment ({node}, std::move (token));
}
-tl::optional<AST::Fragment>
-MacroBuiltin::format_args_handler (location_t invoc_locus,
- AST::MacroInvocData &invoc,
- AST::FormatArgs::Newline nl)
+struct FormatArgsInput
{
- // Remove the delimiters from the macro invocation:
- // the invoc data for `format_args!(fmt, arg1, arg2)` is `(fmt, arg1, arg2)`,
- // so we pop the front and back to remove the parentheses (or curly brackets,
- // or brackets)
- auto tokens = invoc.get_delim_tok_tree ().to_token_stream ();
- tokens.erase (tokens.begin ());
- tokens.pop_back ();
+ std::unique_ptr<AST::Expr> format_str;
+ AST::FormatArguments args;
+ // bool is_literal?
+};
- auto append_newline = nl == AST::FormatArgs::Newline::Yes ? true : false;
- auto fmt_arg
- = parse_single_string_literal (append_newline ? BuiltinMacro::FormatArgsNl
- : BuiltinMacro::FormatArgs,
- invoc.get_delim_tok_tree (), invoc_locus,
- invoc.get_expander ());
+struct FormatArgsParseError
+{
+ enum class Kind
+ {
+ MissingArguments
+ } kind;
+};
+
+static tl::expected<FormatArgsInput, FormatArgsParseError>
+format_args_parse_arguments (AST::MacroInvocData &invoc)
+{
+ MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
+ Parser<MacroInvocLexer> parser (lex);
+
+ // TODO: check if EOF - return that format_args!() requires at least one
+ // argument
- if (!fmt_arg->is_literal ())
+ auto args = AST::FormatArguments ();
+ auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
+ std::unique_ptr<AST::Expr> format_str = nullptr;
+
+ // TODO: Handle the case where we're not parsing a string literal (macro
+ // invocation for e.g.)
+ if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
+ format_str = parser.parse_literal_expr ();
+
+ // TODO: Allow implicit captures ONLY if the the first arg is a string literal
+ // and not a macro invocation
+
+ // TODO: How to consume all of the arguments until the delimiter?
+
+ // TODO: What we then want to do is as follows:
+ // for each token, check if it is an identifier
+ // yes? is the next token an equal sign (=)
+ // yes?
+ // -> if that identifier is already present in our map, error
+ // out
+ // -> parse an expression, return a FormatArgument::Named
+ // no?
+ // -> if there have been named arguments before, error out
+ // (positional after named error)
+ // -> parse an expression, return a FormatArgument::Normal
+ while (parser.peek_current_token ()->get_id () != last_token_id)
{
- rust_sorry_at (
- invoc_locus,
- "cannot yet use eager macro invocations as format strings");
- return AST::Fragment::create_empty ();
+ parser.skip_token (COMMA);
+
+ if (parser.peek_current_token ()->get_id () == IDENTIFIER
+ && parser.peek (1)->get_id () == EQUAL)
+ {
+ // FIXME: This is ugly - just add a parser.parse_identifier()?
+ auto ident_tok = parser.peek_current_token ();
+ auto ident = Identifier (ident_tok);
+
+ parser.skip_token (IDENTIFIER);
+ parser.skip_token (EQUAL);
+
+ auto expr = parser.parse_expr ();
+
+ // TODO: Handle graciously
+ if (!expr)
+ rust_unreachable ();
+
+ args.push (AST::FormatArgument::named (ident, std::move (expr)));
+ }
+ else
+ {
+ auto expr = parser.parse_expr ();
+
+ // TODO: Handle graciously
+ if (!expr)
+ rust_unreachable ();
+
+ args.push (AST::FormatArgument::normal (std::move (expr)));
+ }
+ // we need to skip commas, don't we?
}
+ return FormatArgsInput{std::move (format_str), std::move (args)};
+}
+
+tl::optional<AST::Fragment>
+MacroBuiltin::format_args_handler (location_t invoc_locus,
+ AST::MacroInvocData &invoc,
+ AST::FormatArgs::Newline nl)
+{
+ auto input = format_args_parse_arguments (invoc);
+
+ // auto fmt_arg
+ // // FIXME: this eneds to be split up into a smaller function
+ // = parse_single_string_literal (append_newline ?
+ // BuiltinMacro::FormatArgsNl
+ // : BuiltinMacro::FormatArgs,
+ // invoc.get_delim_tok_tree (), invoc_locus,
+ // invoc.get_expander ());
+
+ // if (!fmt_arg->is_literal ())
+ // {
+ // rust_sorry_at (
+ // invoc_locus,
+ // "cannot yet use eager macro invocations as format strings");
+ // return AST::Fragment::create_empty ();
+ // }
+
// FIXME: We need to handle this
// // if it is not a literal, it's an eager macro invocation - return it
// if (!fmt_expr->is_literal ())
// token_tree.to_token_stream ());
// }
- auto fmt_str = static_cast<AST::LiteralExpr &> (*fmt_arg.get ());
+ // auto fmt_str = static_cast<AST::LiteralExpr &> (*fmt_arg.get ());
// Switch on the format string to know if the string is raw or cooked
- switch (fmt_str.get_lit_type ())
- {
- // case AST::Literal::RAW_STRING:
- case AST::Literal::STRING:
- break;
- case AST::Literal::CHAR:
- case AST::Literal::BYTE:
- case AST::Literal::BYTE_STRING:
- case AST::Literal::INT:
- case AST::Literal::FLOAT:
- case AST::Literal::BOOL:
- case AST::Literal::ERROR:
- rust_unreachable ();
- }
+ // switch (fmt_str.get_lit_type ())
+ // {
+ // // case AST::Literal::RAW_STRING:
+ // case AST::Literal::STRING:
+ // break;
+ // case AST::Literal::CHAR:
+ // case AST::Literal::BYTE:
+ // case AST::Literal::BYTE_STRING:
+ // case AST::Literal::INT:
+ // case AST::Literal::FLOAT:
+ // case AST::Literal::BOOL:
+ // case AST::Literal::ERROR:
+ // rust_unreachable ();
+ // }
+
+ // Remove the delimiters from the macro invocation:
+ // the invoc data for `format_args!(fmt, arg1, arg2)` is `(fmt, arg1, arg2)`,
+ // so we pop the front and back to remove the parentheses (or curly brackets,
+ // or brackets)
+ auto tokens = invoc.get_delim_tok_tree ().to_token_stream ();
+ tokens.erase (tokens.begin ());
+ tokens.pop_back ();
std::stringstream stream;
for (const auto &tok : tokens)
stream << tok->as_string () << ' ';
- rust_debug ("[ARTHUR]: `%s`", stream.str ().c_str ());
-
- auto pieces = Fmt::Pieces::collect (stream.str ());
+ auto append_newline = nl == AST::FormatArgs::Newline::Yes ? true : false;
+ auto pieces = Fmt::Pieces::collect (stream.str (), append_newline);
// TODO:
// do the transformation into an AST::FormatArgs node
// return that
// expand it during lowering
- return AST::Fragment::create_empty ();
+ // TODO: we now need to take care of creating `unfinished_literal`? this is
+ // for creating the `template`
+
+ auto fmt_args_node = new AST::FormatArgs (invoc_locus, std::move (pieces),
+ std::move (input->args));
+ auto node = std::unique_ptr<AST::Expr> (fmt_args_node);
+ auto single_node = AST::SingleASTNode (std::move (node));
+
+ return AST::Fragment ({std::move (single_node)},
+ invoc.get_delim_tok_tree ().to_token_stream ());
}
tl::optional<AST::Fragment>
#include "rust-ast-lower-extern.h"
#include "rust-ast.h"
#include "rust-attribute-values.h"
+#include "rust-diagnostics.h"
#include "rust-item.h"
#include "rust-system.h"
fn main() {
dbg!(rust::collect_pieces(
- std::env::args().nth(1).unwrap().as_str()
+ std::env::args().nth(1).unwrap().as_str(),
+ None,
+ None,
+ false
));
}
/// A piece is a portion of the format string which represents the next part
/// to emit. These are emitted as a stream by the `Parser` class.
- #[derive(Clone, Debug, PartialEq)]
+ #[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub enum Piece<'a> {
/// A literal string which should directly be emitted
String(&'a str),
/// This describes that formatting should process the next argument (as
/// specified inside) for emission.
- NextArgument(*const Argument<'a>),
- }
-
- impl<'a> Drop for Piece<'a> {
- fn drop(&mut self) {
- println!("dropping Piece: {:?}", self)
- }
+ // do we need a pointer here? we're doing big cloning anyway
+ NextArgument(Argument<'a>),
}
/// Representation of an argument specification.
let ptr = Box::leak(x);
let dst = Into::<Argument>::into(*ptr);
- Piece::NextArgument(&dst as *const Argument)
+ Piece::NextArgument(dst)
}
}
}
pub mod rust {
use generic_format_parser::{ParseMode, Parser, Piece};
- pub fn collect_pieces(input: &str) -> Vec<Piece<'_>> {
- let parser = Parser::new(input, None, None, true, ParseMode::Format);
+ pub fn collect_pieces(
+ input: &str,
+ style: Option<usize>,
+ snippet: Option<String>,
+ append_newline: bool,
+ ) -> Vec<Piece<'_>> {
+ let parser = Parser::new(input, style, snippet, append_newline, ParseMode::Format);
parser.into_iter().collect()
}
}
#[no_mangle]
-pub extern "C" fn collect_pieces(input: *const libc::c_char) -> PieceSlice {
+pub extern "C" fn collect_pieces(input: *const libc::c_char, append_newline: bool) -> PieceSlice {
+ dbg!(input);
+
// FIXME: Add comment
let str = unsafe { CStr::from_ptr(input) };
- dbg!(str);
// FIXME: No unwrap
- let pieces: Vec<ffi::Piece<'_>> = rust::collect_pieces(str.to_str().unwrap())
- .into_iter()
- .map(Into::into)
- .collect();
+ let pieces: Vec<ffi::Piece<'_>> =
+ rust::collect_pieces(str.to_str().unwrap(), None, None, append_newline)
+ .into_iter()
+ .map(Into::into)
+ .collect();
println!("[ARTHUR]: debug: {:?}, {:?}", pieces.as_ptr(), pieces.len());
}
#[no_mangle]
-pub extern "C" fn destroy_pieces(PieceSlice { base_ptr, len, cap }: PieceSlice) {
- let _ = unsafe { Vec::from_raw_parts(base_ptr, len, cap) };
+pub unsafe extern "C" fn destroy_pieces(PieceSlice { base_ptr, len, cap }: PieceSlice) {
+ eprintln!("[ARTHUR] destroying pieces: {base_ptr:?} {len} {cap}");
+ drop(Vec::from_raw_parts(base_ptr, len, cap));
+}
+
+#[no_mangle]
+pub extern "C" fn clone_pieces(
+ base_ptr: *mut ffi::Piece<'static>,
+ len: usize,
+ cap: usize,
+) -> PieceSlice {
+ eprintln!("[ARTHUR] cloning pieces: {base_ptr:?} {len} {cap}");
+
+ let v = unsafe { Vec::from_raw_parts(base_ptr, len, cap) };
+
+ let cloned_v = v.clone();
+
+ // FIXME: Add documentation
+ v.leak();
+
+ PieceSlice {
+ len: cloned_v.len(),
+ cap: cloned_v.capacity(),
+ base_ptr: dbg!(cloned_v.leak().as_mut_ptr()),
+ }
}