diff --git a/genco-macros/Cargo.toml b/genco-macros/Cargo.toml index c199de9..549f27b 100644 --- a/genco-macros/Cargo.toml +++ b/genco-macros/Cargo.toml @@ -20,8 +20,5 @@ syn = { version = "2.0.38", features = ["full"] } q = { package = "quote", version = "1.0.3" } proc-macro2 = { version = "1.0.10", features = ["span-locations"] } -[dev-dependencies] -genco = { path = "..", version = "0.17.7" } - [lib] proc-macro = true diff --git a/genco-macros/src/lib.rs b/genco-macros/src/lib.rs index 9679d84..fd87f58 100644 --- a/genco-macros/src/lib.rs +++ b/genco-macros/src/lib.rs @@ -44,642 +44,7 @@ mod quote_in; mod requirements; mod static_buffer; mod string_parser; -mod token; -/// Whitespace sensitive quasi-quoting. -/// -/// This and the [quote_in!] macro is the thing that this library revolves -/// around. -/// -/// It provides a flexible and intuitive mechanism for efficiently generating -/// beautiful code directly inside of Rust. -/// -/// > Note that this macro **can only detect line changes** if it's built under -/// > a `nightly` compiler. See the [main genco documentation] for more -/// > information. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let hash_map = &dart::import("dart:collection", "HashMap"); -/// -/// let tokens: dart::Tokens = quote! { -/// print_greeting(String name) { -/// print($[str](Hello $(name))); -/// } -/// -/// $hash_map map() { -/// return new $hash_map(); -/// } -/// }; -/// -/// println!("{}", tokens.to_file_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -/// # Interpolation -/// -/// Variables are interpolated using `$`, so to include the variable `test`, you -/// would write `$test`. Interpolated variables must implement [FormatInto]. -/// Expressions can be interpolated with `$()`. -/// -/// > *Note:* The `$` punctuation itself can be escaped by repeating it twice. -/// > So `$$` would produce a single `$` token. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let hash_map = rust::import("std::collections", "HashMap"); -/// -/// let tokens: rust::Tokens = quote! { -/// struct Quoted { -/// field: $hash_map, -/// } -/// }; -/// -/// assert_eq!( -/// vec![ -/// "use std::collections::HashMap;", -/// "", -/// "struct Quoted {", -/// " field: HashMap,", -/// "}", -/// ], -/// tokens.to_file_vec()?, -/// ); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// The following is an expression interpolated with `$()`. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let tokens: genco::Tokens = quote! { -/// hello $("world".to_uppercase()) -/// }; -/// -/// assert_eq!("hello WORLD", tokens.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// Interpolations are evaluated in the same scope as the macro, so you can -/// freely make use of Rust operations like the try keyword (`?`) if -/// appropriate: -/// -/// ``` -/// use std::error::Error; -/// -/// use genco::prelude::*; -/// -/// fn age_fn(age: &str) -> Result> { -/// Ok(quote! { -/// fn age() { -/// println!("You are {} years old!", $(str::parse::(age)?)); -/// } -/// }) -/// } -/// ``` -/// -/// [FormatInto]: https://docs.rs/genco/0/genco/tokens/trait.FormatInto.html -/// [main genco documentation]: https://docs.rs/genco -/// -///
-/// -/// # Escape Sequences -/// -/// Because this macro is *whitespace sensitive*, it might sometimes be -/// necessary to provide hints of where whitespace should be inserted. -/// -/// `quote!` trims any trailing and leading whitespace that it sees. So -/// `quote!(Hello )` is the same as `quote!(Hello)`. To include a space at the -/// end, we can use the special `$[' ']` escape sequence: -/// `quote!(Hello$[' '])`. -/// -/// The available escape sequences are: -/// -/// * `$[' ']` — Inserts spacing between tokens. This corresponds to the -/// [Tokens::space] function. -/// -/// * `$['\r']` — Inserts a push operation. Push operations makes sure that -/// any following tokens are on their own dedicated line. This corresponds to -/// the [Tokens::push] function. -/// -/// * `$['\n']` — Inserts a forced line. Line operations makes sure that any -/// following tokens have an empty line separating them. This corresponds to -/// the [Tokens::line] function. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let numbers = 3..=5; -/// -/// let tokens: Tokens<()> = quote!(foo$['\r']bar$['\n']baz$[' ']biz); -/// -/// assert_eq!("foo\nbar\n\nbaz biz", tokens.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// [Tokens::space]: https://docs.rs/genco/0/genco/struct.Tokens.html#method.space -/// [Tokens::push]: https://docs.rs/genco/0/genco/struct.Tokens.html#method.push -/// [Tokens::line]: https://docs.rs/genco/0/genco/struct.Tokens.html#method.line -/// -/// # String Quoting -/// -/// Literal strings like `"hello"` are automatically quoted for the target -/// language according to its [Lang::write_quoted] implementation. -/// -/// [Lang::write_quoted]: https://docs.rs/genco/0/genco/lang/trait.Lang.html#method.write_quoted -/// -/// ``` -/// use genco::prelude::*; -/// -/// let tokens: java::Tokens = quote! { -/// "hello world 😊" -/// $(quoted("hello world 😊")) -/// $("\"hello world 😊\"") -/// $[str](hello world $[const]("😊")) -/// }; -/// -/// assert_eq!( -/// vec![ -/// "\"hello world \\ud83d\\ude0a\"", -/// "\"hello world \\ud83d\\ude0a\"", -/// "\"hello world 😊\"", -/// "\"hello world \\ud83d\\ude0a\"", -/// ], -/// tokens.to_file_vec()?, -/// ); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -/// # Efficient String Quoting -/// -/// It's worth investigating the different forms of tokens produced by the -/// above example. -/// -/// * The first one is a static *quoted string*. -/// * The second one is a boxed *quoted string*, who's content will be copied -/// and is stored on the heap. -/// * The third one is a static *literal* which bypasses language quoting -/// entirely. -/// * Finally the fourth one is an interpolated string. They are really neat, -/// and will be covered more in the next section. It's worth noting that -/// `$("😊")` is used, because 😊 is not a valid identifier in Rust. So this -/// example showcases how strings can be directly embedded in an -/// interpolation. -/// -/// Here you can see the items produced by the macro. -/// -/// ``` -/// # use genco::prelude::*; -/// # let tokens: rust::Tokens = quote! { -/// # "hello world 😊" -/// # $(quoted("hello world 😊")) -/// # $("\"hello world 😊\"") -/// # $[str](hello world $[const]("😊")) -/// # }; -/// use genco::tokens::{Item, ItemStr}; -/// -/// assert_eq!( -/// vec![ -/// Item::OpenQuote(false), -/// Item::Literal(ItemStr::Static("hello world 😊")), -/// Item::CloseQuote, -/// Item::Push, -/// Item::OpenQuote(false), -/// Item::Literal(ItemStr::Box("hello world 😊".into())), -/// Item::CloseQuote, -/// Item::Push, -/// Item::Literal(ItemStr::Static("\"hello world 😊\"")), -/// Item::Push, -/// Item::OpenQuote(false), -/// Item::Literal(ItemStr::Static("hello world 😊")), -/// Item::CloseQuote -/// ], -/// tokens, -/// ); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// # Quoted String Interpolation -/// -/// Some languages support interpolating values into strings. -/// -/// Examples of these are: -/// -/// * JavaScript - With [template literals] `` `Hello ${a}` `` (note the -/// backticks). -/// * Dart - With [interpolated strings] like `"Hello $a"` or `"Hello ${a + -/// b}"`. -/// -/// The [quote!] macro supports this through `$[str]()`. This will -/// produce literal strings with the appropriate language-specific quoting and -/// string interpolation formats used. -/// -/// Components of the string are runtime evaluated with the typical variable -/// escape sequences `$ident`, `$()`. In order to interpolate the string -/// at compile time we can instead make use of `$[const]()` like you can see with the smile below: -/// -/// ``` -/// use genco::prelude::*; -/// -/// let smile = "😊"; -/// -/// let t: js::Tokens = quote!($[str](Hello $[const](smile) $world)); -/// assert_eq!("`Hello 😊 ${world}`", t.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -/// Interpolated values are specified with `$()`. And `$` itself is -/// escaped by repeating it twice through `$$`. The `` section is -/// interpreted the same as in the [quote!] macro, but is whitespace sensitive. -/// This means that `$(foo)` is not the same as `$(foo )` since the latter will -/// have a space preserved at the end. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let smile = "😊"; -/// -/// let t: dart::Tokens = quote!($[str](Hello $[const](smile) $(world))); -/// assert_eq!("\"Hello 😊 $world\"", t.to_string()?); -/// -/// let t: dart::Tokens = quote!($[str](Hello $[const](smile) $(a + b))); -/// assert_eq!("\"Hello 😊 ${a + b}\"", t.to_string()?); -/// -/// let t: js::Tokens = quote!($[str](Hello $[const](smile) $(world))); -/// assert_eq!("`Hello 😊 ${world}`", t.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// [template literals]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals -/// [interpolated strings]: https://medium.com/run-dart/dart-dartlang-introduction-string-interpolation-8ed99174119a -/// -/// # Control Flow -/// -/// [quote!] provides some limited mechanisms for control flow inside of the -/// macro for convenience. The supported mechanisms are: -/// -/// * [Loops](#loops) - `$(for in [join ()] => )`. -/// * [Conditionals](#conditionals) - `$(if => )`. -/// * [Match Statements](#match-statements) - `$(match { [ => ,]* })`. -/// -///
-/// -/// # Loops -/// -/// To repeat a pattern you can use `$(for in { })`, -/// where `` is an iterator. -/// -/// It is also possible to use the more compact `$(for in => -/// )` (note the arrow). -/// -/// `` will be treated as a quoted expression, so anything which works -/// during regular quoting will work here as well, with the addition that -/// anything defined in `` will be made available to the statement. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let numbers = 3..=5; -/// -/// let tokens: Tokens<()> = quote! { -/// Your numbers are: $(for n in numbers => $n$[' ']) -/// }; -/// -/// assert_eq!("Your numbers are: 3 4 5", tokens.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// # Joining Loops -/// -/// You can add `join ()` to the end of a repetition. -/// -/// The expression specified in `join ()` is added _between_ each -/// element produced by the loop. -/// -/// > *Note:* The argument to `join` is *whitespace sensitive*, so leading and -/// > trailing is preserved. `join (,)` and `join (, )` would therefore produce -/// > different results. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let numbers = 3..=5; -/// -/// let tokens: Tokens<()> = quote! { -/// Your numbers are: $(for n in numbers join (, ) => $n). -/// }; -/// -/// assert_eq!("Your numbers are: 3, 4, 5.", tokens.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// [quote!]: macro.quote.html -/// -/// # Conditionals -/// -/// You can specify a conditional with `$(if => )` where -/// is an pattern or expression evaluating to a `bool`, and `` -/// is a quoted expressions. -/// -/// It's also possible to specify a condition with an else branch, by using -/// `$(if { } else { })`. `` is also a quoted -/// expression. -/// -/// ``` -/// use genco::prelude::*; -/// -/// fn greeting(hello: bool, name: &str) -> Tokens<()> { -/// quote!(Custom Greeting: $(if hello { -/// Hello $name -/// } else { -/// Goodbye $name -/// })) -/// } -/// -/// let tokens = greeting(true, "John"); -/// assert_eq!("Custom Greeting: Hello John", tokens.to_string()?); -/// -/// let tokens = greeting(false, "John"); -/// assert_eq!("Custom Greeting: Goodbye John", tokens.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// The `` branch is optional, conditionals which do not have an else -/// branch and evaluated to `false` won't produce any tokens: -/// -/// ``` -/// use genco::prelude::*; -/// -/// fn greeting(hello: bool, name: &str) -> Tokens<()> { -/// quote!(Custom Greeting:$(if hello { -/// $[' ']Hello $name -/// })) -/// } -/// -/// let tokens = greeting(true, "John"); -/// assert_eq!("Custom Greeting: Hello John", tokens.to_string()?); -/// -/// let tokens = greeting(false, "John"); -/// assert_eq!("Custom Greeting:", tokens.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// # Match Statements -/// -/// You can specify a match expression using `$(match { [ => -/// ,]* }`, where `` is an evaluated expression that is match -/// against each subsequent ``. If a pattern matches, the arm with the -/// matching `` block is evaluated. -/// -/// ``` -/// use genco::prelude::*; -/// -/// fn greeting(name: &str) -> Tokens<()> { -/// quote!(Hello $(match name { -/// "John" | "Jane" => $("Random Stranger"), -/// other => $other, -/// })) -/// } -/// -/// let tokens = greeting("John"); -/// assert_eq!("Hello Random Stranger", tokens.to_string()?); -/// -/// let tokens = greeting("Mio"); -/// assert_eq!("Hello Mio", tokens.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -/// If a match arm contains parenthesis (`=> ()`), the expansion will be -/// *whitespace sensitive*. Allowing leading and trailing whitespace to be -/// preserved: -/// -/// ``` -/// use genco::prelude::*; -/// -/// fn greeting(name: &str) -> Tokens<()> { -/// quote!(Hello$(match name { -/// "John" | "Jane" => ( $("Random Stranger")), -/// other => ( $other), -/// })) -/// } -/// -/// let tokens = greeting("John"); -/// assert_eq!("Hello Random Stranger", tokens.to_string()?); -/// -/// let tokens = greeting("Mio"); -/// assert_eq!("Hello Mio", tokens.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -/// The following is an example with more complex matching: -/// -/// ``` -/// use genco::prelude::*; -/// -/// enum Greeting { -/// Named(&'static str), -/// Unknown, -/// } -/// -/// fn greeting(name: Greeting) -> Tokens<()> { -/// quote!(Hello $(match name { -/// Greeting::Named("John") | Greeting::Named("Jane") => $("Random Stranger"), -/// Greeting::Named(other) => $other, -/// Greeting::Unknown => $("Unknown Person"), -/// })) -/// } -/// -/// let tokens = greeting(Greeting::Named("John")); -/// assert_eq!("Hello Random Stranger", tokens.to_string()?); -/// -/// let tokens = greeting(Greeting::Unknown); -/// assert_eq!("Hello Unknown Person", tokens.to_string()?); -/// -/// let tokens = greeting(Greeting::Named("Mio")); -/// assert_eq!("Hello Mio", tokens.to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// # Scopes -/// -/// You can use `$(ref { })` to gain access to the current -/// token stream. This is an alternative to existing control flow operators if -/// you want to run some custom code during evaluation which is otherwise not -/// supported. This is called a *scope*. -/// -/// For a more compact variant you can omit the braces with `$(ref => -/// )`. -/// -/// ``` -/// use genco::prelude::*; -/// -/// fn quote_greeting(surname: &str, lastname: Option<&str>) -> rust::Tokens { -/// quote! { -/// Hello $surname$(ref toks { -/// if let Some(lastname) = lastname { -/// toks.space(); -/// toks.append(lastname); -/// } -/// }) -/// } -/// } -/// -/// assert_eq!("Hello John", quote_greeting("John", None).to_string()?); -/// assert_eq!("Hello John Doe", quote_greeting("John", Some("Doe")).to_string()?); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// ## Whitespace Detection -/// -/// The [quote!] macro has the following rules for dealing with indentation and -/// spacing. -/// -/// **Spaces** — Two tokens that are separated are spaced. Regardless of how -/// many spaces there are between them. This can be controlled manually by -/// inserting the [`$[' ']`][escape] escape sequence in the token stream. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let tokens: rust::Tokens = quote! { -/// fn test() { -/// println!("Hello... "); -/// -/// println!("World!"); -/// } -/// }; -/// -/// assert_eq!( -/// vec![ -/// "fn test() {", -/// " println!(\"Hello... \");", -/// "", -/// " println!(\"World!\");", -/// "}", -/// ], -/// tokens.to_file_vec()?, -/// ); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// **Line breaking** — Line breaks are detected by leaving two empty lines -/// between two tokens. This can be controlled manually by inserting the -/// [`$['\n']`][escape] escape in the token stream. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let tokens: rust::Tokens = quote! { -/// fn test() { -/// println!("Hello... "); -/// -/// -/// -/// println!("World!"); -/// } -/// }; -/// -/// assert_eq!( -/// vec![ -/// "fn test() {", -/// " println!(\"Hello... \");", -/// "", -/// " println!(\"World!\");", -/// "}", -/// ], -/// tokens.to_file_vec()?, -/// ); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -///
-/// -/// **Indentation** — Indentation is determined on a row-by-row basis. If a -/// column is further in than the one on the preceeding row, it is indented *one -/// level* deeper. -/// -/// If a column starts shallower than a preceeding, non-whitespace only row, it -/// will be matched against previously known indentation levels. Failure to -/// match a previously known level is an error. -/// -/// All indentations inserted during the macro will be unrolled at the end of -/// it. So any trailing indentations will be matched by unindentations. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let tokens: rust::Tokens = quote! { -/// fn test() { -/// println!("Hello... "); -/// -/// println!("World!"); -/// } -/// }; -/// -/// assert_eq!( -/// vec![ -/// "fn test() {", -/// " println!(\"Hello... \");", -/// "", -/// " println!(\"World!\");", -/// "}", -/// ], -/// tokens.to_file_vec()?, -/// ); -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -/// Example showcasing an indentation mismatch: -/// -/// ```,compile_fail -/// use genco::prelude::*; -/// -/// let tokens: rust::Tokens = quote! { -/// fn test() { -/// println!("Hello... "); -/// -/// println!("World!"); -/// } -/// }; -/// ``` -/// -/// ```text -/// ---- src\lib.rs - (line 150) stdout ---- -/// error: expected 4 less spaces of indentation -/// --> src\lib.rs:157:9 -/// | -/// 10 | println!("World!"); -/// | ^^^^^^^ -/// ``` -/// -/// [escape]: #escape-sequences #[proc_macro] pub fn quote(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let cx = Ctxt::default(); @@ -711,153 +76,12 @@ pub fn quote(input: proc_macro::TokenStream) -> proc_macro::TokenStream { gen.into() } -/// Behaves the same as [quote!] while quoting into an existing token stream -/// with ` => `. -/// -/// This macro takes a destination stream followed by an `=>` and the tokens to -/// extend that stream with. -/// -/// Note that the `` arguments must be borrowable. So a mutable -/// reference like `&mut rust::Tokens` will have to be dereferenced when used -/// with this macro. -/// -/// ``` -/// # use genco::prelude::*; -/// -/// # fn generate() -> rust::Tokens { -/// let mut tokens = rust::Tokens::new(); -/// quote_in!(tokens => hello world); -/// # tokens -/// # } -/// -/// fn generate_into(tokens: &mut rust::Tokens) { -/// quote_in! { *tokens => -/// hello... -/// world! -/// }; -/// } -/// ``` -/// -/// [quote!]: macro.quote.html -/// -/// # Example -/// -/// ``` -/// use genco::prelude::*; -/// -/// let mut tokens = rust::Tokens::new(); -/// -/// quote_in! { tokens => -/// fn foo() -> u32 { -/// 42 -/// } -/// } -/// ``` -/// -/// # Use with scopes -/// -/// [quote_in!] can be used inside of a [quote!] through [a scope]. -/// -/// ``` -/// use genco::prelude::*; -/// -/// let tokens: rust::Tokens = quote! { -/// fn foo(v: bool) -> u32 { -/// $(ref out { -/// quote_in! { *out => -/// if v { -/// 1 -/// } else { -/// 0 -/// } -/// } -/// }) -/// } -/// }; -/// ``` -/// -/// [quote_in!]: macro.quote_in.html -/// [quote!]: macro.quote.html -/// [a scope]: macro.quote.html#scopes #[proc_macro] pub fn quote_in(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let quote_in = syn::parse_macro_input!(input as quote_in::QuoteIn); quote_in.stream.into() } -/// Convenience macro for constructing a [FormatInto] implementation in-place. -/// -/// Constructing [FormatInto] implementation instead of short lived -/// [token streams] can be more beneficial for memory use and performance. -/// -/// [FormatInto]: https://docs.rs/genco/0/genco/tokens/trait.FormatInto.html -/// [token streams]: https://docs.rs/genco/0/genco/struct.Tokens.html -/// -/// # Comparison -/// -/// In the below example, `f1` and `f2` are equivalent. In here [quote_fn!] -/// simply makes it easier to build. -/// -/// ``` -/// use genco::prelude::*; -/// use genco::tokens::from_fn; -/// -/// let f1 = from_fn(move |t| { -/// quote_in!{ *t => -/// println!("Hello World"); -/// } -/// }); -/// -/// let f2 = quote_fn!{ -/// println!("Hello World"); -/// }; -/// -/// let tokens: rust::Tokens = quote!{ -/// $f1 -/// $f2 -/// }; -/// -/// assert_eq!{ -/// vec![ -/// "println!(\"Hello World\");", -/// "println!(\"Hello World\");", -/// ], -/// tokens.to_file_vec()?, -/// }; -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` -/// -/// # Examples which borrow -/// -/// ``` -/// use genco::prelude::*; -/// -/// fn greeting(name: &str) -> impl FormatInto + '_ { -/// quote_fn! { -/// println!($[str](Hello $[const](name))) -/// } -/// } -/// -/// fn advanced_greeting<'a>(first: &'a str, last: &'a str) -> impl FormatInto + 'a { -/// quote_fn! { -/// println!($[str](Hello $[const](first) $[const](last))) -/// } -/// } -/// -/// let tokens = quote! { -/// $(greeting("Mio")); -/// $(advanced_greeting("Jane", "Doe")); -/// }; -/// -/// assert_eq!{ -/// vec![ -/// "println!(\"Hello Mio\");", -/// "println!(\"Hello Jane Doe\");", -/// ], -/// tokens.to_file_vec()? -/// }; -/// # Ok::<_, genco::fmt::Error>(()) -/// ``` #[proc_macro] pub fn quote_fn(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let quote_fn = syn::parse_macro_input!(input as quote_fn::QuoteFn); diff --git a/genco-macros/src/quote.rs b/genco-macros/src/quote.rs index 31473f0..b66fba3 100644 --- a/genco-macros/src/quote.rs +++ b/genco-macros/src/quote.rs @@ -302,9 +302,8 @@ impl<'a> Quote<'a> { ast } else if scope.peek(Token![ref]) { self.parse_scope(&scope)? - } else if scope.peek(syn::LitStr) && scope.peek2(crate::token::Eof) { + } else if crate::string_parser::is_lit_str_opt(scope.fork())? { let string = scope.parse::()?.value(); - Ast::Literal { string } } else { Ast::Eval { diff --git a/genco-macros/src/string_parser.rs b/genco-macros/src/string_parser.rs index 2376a33..543a4a8 100644 --- a/genco-macros/src/string_parser.rs +++ b/genco-macros/src/string_parser.rs @@ -4,7 +4,7 @@ use std::cell::{Cell, RefCell}; use std::fmt::Write; use proc_macro2::{Span, TokenStream, TokenTree}; -use syn::parse::ParseStream; +use syn::parse::{ParseBuffer, ParseStream}; use syn::spanned::Spanned; use syn::token; use syn::Result; @@ -255,7 +255,7 @@ impl<'a> StringParser<'a> { // Compile-time string optimization. A single, // enclosed literal string can be added to the // existing static buffer. - if content.peek(syn::LitStr) && content.peek2(crate::token::Eof) { + if is_lit_str_opt(content.fork())? { let s = content.parse::()?; encoder.encode_str(&s.value(), start.start, Some(end.end))?; } else { @@ -308,3 +308,11 @@ impl<'a> StringParser<'a> { Ok((options, requirements, stream)) } } + +pub(crate) fn is_lit_str_opt(content: ParseBuffer<'_>) -> syn::Result { + if content.parse::>()?.is_none() { + return Ok(false); + } + + Ok(content.is_empty()) +} diff --git a/genco-macros/src/token.rs b/genco-macros/src/token.rs deleted file mode 100644 index 9352a07..0000000 --- a/genco-macros/src/token.rs +++ /dev/null @@ -1,19 +0,0 @@ -/// Token allowing you to peek for anything. -/// -/// Can be used to peek for end of stream with `peek`, `peek2`, and `peek3`. -pub(crate) struct Eof {} - -#[allow(non_snake_case)] -pub(crate) fn Eof(_: T) -> Eof { - Eof {} -} - -impl syn::token::CustomToken for Eof { - fn peek(cursor: syn::buffer::Cursor<'_>) -> bool { - cursor.eof() - } - - fn display() -> &'static str { - "" - } -} diff --git a/src/lib.rs b/src/lib.rs index cd56fae..5285057 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -148,8 +148,6 @@ //! [quote strings]: https://docs.rs/genco/latest/genco/macro.quote.html#string-quoting //! [interpolate]: https://docs.rs/genco/latest/genco/macro.quote.html#quoted-string-interpolation //! [whitespace detection]: https://docs.rs/genco/latest/genco/macro.quote.html#whitespace-detection -//! [quote!]: https://docs.rs/genco/latest/genco/macro.quote.html -//! [quote_in!]: https://docs.rs/genco/latest/genco/macro.quote_in.html //! [impl_lang!]: https://docs.rs/genco/latest/genco/macro.impl_lang.html //! [quoted()]: https://docs.rs/genco/latest/genco/tokens/fn.quoted.html //! [Open an issue!]: https://github.com/udoprog/genco/issues/new @@ -158,7 +156,776 @@ #![deny(rustdoc::broken_intra_doc_links)] #![allow(clippy::needless_doctest_main)] -pub use genco_macros::{quote, quote_fn, quote_in}; +/// Whitespace sensitive quasi-quoting. +/// +/// This and the [quote_in!] macro is the thing that this library revolves +/// around. +/// +/// It provides a flexible and intuitive mechanism for efficiently generating +/// beautiful code directly inside of Rust. +/// +/// > Note that this macro **can only detect line changes** if it's built under +/// > a `nightly` compiler. See the [main genco documentation] for more +/// > information. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let hash_map = &dart::import("dart:collection", "HashMap"); +/// +/// let tokens: dart::Tokens = quote! { +/// print_greeting(String name) { +/// print($[str](Hello $(name))); +/// } +/// +/// $hash_map map() { +/// return new $hash_map(); +/// } +/// }; +/// +/// println!("{}", tokens.to_file_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +/// # Interpolation +/// +/// Variables are interpolated using `$`, so to include the variable `test`, you +/// would write `$test`. Interpolated variables must implement [FormatInto]. +/// Expressions can be interpolated with `$()`. +/// +/// > *Note:* The `$` punctuation itself can be escaped by repeating it twice. +/// > So `$$` would produce a single `$` token. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let hash_map = rust::import("std::collections", "HashMap"); +/// +/// let tokens: rust::Tokens = quote! { +/// struct Quoted { +/// field: $hash_map, +/// } +/// }; +/// +/// assert_eq!( +/// vec![ +/// "use std::collections::HashMap;", +/// "", +/// "struct Quoted {", +/// " field: HashMap,", +/// "}", +/// ], +/// tokens.to_file_vec()?, +/// ); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// The following is an expression interpolated with `$()`. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let tokens: genco::Tokens = quote! { +/// hello $("world".to_uppercase()) +/// }; +/// +/// assert_eq!("hello WORLD", tokens.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// Interpolations are evaluated in the same scope as the macro, so you can +/// freely make use of Rust operations like the try keyword (`?`) if +/// appropriate: +/// +/// ``` +/// use std::error::Error; +/// +/// use genco::prelude::*; +/// +/// fn age_fn(age: &str) -> Result> { +/// Ok(quote! { +/// fn age() { +/// println!("You are {} years old!", $(str::parse::(age)?)); +/// } +/// }) +/// } +/// ``` +/// +/// [FormatInto]: crate::tokens::FormatInto +/// [main genco documentation]: https://docs.rs/genco +/// +///
+/// +/// # Escape Sequences +/// +/// Because this macro is *whitespace sensitive*, it might sometimes be +/// necessary to provide hints of where whitespace should be inserted. +/// +/// `quote!` trims any trailing and leading whitespace that it sees. So +/// `quote!(Hello )` is the same as `quote!(Hello)`. To include a space at the +/// end, we can use the special `$[' ']` escape sequence: +/// `quote!(Hello$[' '])`. +/// +/// The available escape sequences are: +/// +/// * `$[' ']` — Inserts spacing between tokens. This corresponds to the +/// [Tokens::space] function. +/// +/// * `$['\r']` — Inserts a push operation. Push operations makes sure that +/// any following tokens are on their own dedicated line. This corresponds to +/// the [Tokens::push] function. +/// +/// * `$['\n']` — Inserts a forced line. Line operations makes sure that any +/// following tokens have an empty line separating them. This corresponds to +/// the [Tokens::line] function. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let numbers = 3..=5; +/// +/// let tokens: Tokens<()> = quote!(foo$['\r']bar$['\n']baz$[' ']biz); +/// +/// assert_eq!("foo\nbar\n\nbaz biz", tokens.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// # String Quoting +/// +/// Literal strings like `"hello"` are automatically quoted for the target +/// language according to its [Lang::write_quoted] implementation. +/// +/// [Lang::write_quoted]: crate::lang::Lang::write_quoted +/// +/// ``` +/// use genco::prelude::*; +/// +/// let tokens: java::Tokens = quote! { +/// "hello world 😊" +/// $(quoted("hello world 😊")) +/// $("\"hello world 😊\"") +/// $[str](hello world $[const]("😊")) +/// }; +/// +/// assert_eq!( +/// vec![ +/// "\"hello world \\ud83d\\ude0a\"", +/// "\"hello world \\ud83d\\ude0a\"", +/// "\"hello world 😊\"", +/// "\"hello world \\ud83d\\ude0a\"", +/// ], +/// tokens.to_file_vec()?, +/// ); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +/// # Efficient String Quoting +/// +/// It's worth investigating the different forms of tokens produced by the +/// above example. +/// +/// * The first one is a static *quoted string*. +/// * The second one is a boxed *quoted string*, who's content will be copied +/// and is stored on the heap. +/// * The third one is a static *literal* which bypasses language quoting +/// entirely. +/// * Finally the fourth one is an interpolated string. They are really neat, +/// and will be covered more in the next section. It's worth noting that +/// `$("😊")` is used, because 😊 is not a valid identifier in Rust. So this +/// example showcases how strings can be directly embedded in an +/// interpolation. +/// +/// Here you can see the items produced by the macro. +/// +/// ``` +/// # use genco::prelude::*; +/// # let tokens: rust::Tokens = quote! { +/// # "hello world 😊" +/// # $(quoted("hello world 😊")) +/// # $("\"hello world 😊\"") +/// # $[str](hello world $[const]("😊")) +/// # }; +/// use genco::tokens::{Item, ItemStr}; +/// +/// assert_eq!( +/// vec![ +/// Item::OpenQuote(false), +/// Item::Literal(ItemStr::Static("hello world 😊")), +/// Item::CloseQuote, +/// Item::Push, +/// Item::OpenQuote(false), +/// Item::Literal(ItemStr::Box("hello world 😊".into())), +/// Item::CloseQuote, +/// Item::Push, +/// Item::Literal(ItemStr::Static("\"hello world 😊\"")), +/// Item::Push, +/// Item::OpenQuote(false), +/// Item::Literal(ItemStr::Static("hello world 😊")), +/// Item::CloseQuote +/// ], +/// tokens, +/// ); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// # Quoted String Interpolation +/// +/// Some languages support interpolating values into strings. +/// +/// Examples of these are: +/// +/// * JavaScript - With [template literals] `` `Hello ${a}` `` (note the +/// backticks). +/// * Dart - With [interpolated strings] like `"Hello $a"` or `"Hello ${a + +/// b}"`. +/// +/// The [quote!] macro supports this through `$[str]()`. This will +/// produce literal strings with the appropriate language-specific quoting and +/// string interpolation formats used. +/// +/// Components of the string are runtime evaluated with the typical variable +/// escape sequences `$ident`, `$()`. In order to interpolate the string +/// at compile time we can instead make use of `$[const]()` like you can see with the smile below: +/// +/// ``` +/// use genco::prelude::*; +/// +/// let smile = "😊"; +/// +/// let t: js::Tokens = quote!($[str](Hello $[const](smile) $world)); +/// assert_eq!("`Hello 😊 ${world}`", t.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +/// Interpolated values are specified with `$()`. And `$` itself is +/// escaped by repeating it twice through `$$`. The `` section is +/// interpreted the same as in the [quote!] macro, but is whitespace sensitive. +/// This means that `$(foo)` is not the same as `$(foo )` since the latter will +/// have a space preserved at the end. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let smile = "😊"; +/// +/// let t: dart::Tokens = quote!($[str](Hello $[const](smile) $(world))); +/// assert_eq!("\"Hello 😊 $world\"", t.to_string()?); +/// +/// let t: dart::Tokens = quote!($[str](Hello $[const](smile) $(a + b))); +/// assert_eq!("\"Hello 😊 ${a + b}\"", t.to_string()?); +/// +/// let t: js::Tokens = quote!($[str](Hello $[const](smile) $(world))); +/// assert_eq!("`Hello 😊 ${world}`", t.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// [template literals]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals +/// [interpolated strings]: https://medium.com/run-dart/dart-dartlang-introduction-string-interpolation-8ed99174119a +/// +/// # Control Flow +/// +/// [quote!] provides some limited mechanisms for control flow inside of the +/// macro for convenience. The supported mechanisms are: +/// +/// * [Loops](#loops) - `$(for in [join ()] => )`. +/// * [Conditionals](#conditionals) - `$(if => )`. +/// * [Match Statements](#match-statements) - `$(match { [ => ,]* })`. +/// +///
+/// +/// # Loops +/// +/// To repeat a pattern you can use `$(for in { })`, +/// where `` is an iterator. +/// +/// It is also possible to use the more compact `$(for in => +/// )` (note the arrow). +/// +/// `` will be treated as a quoted expression, so anything which works +/// during regular quoting will work here as well, with the addition that +/// anything defined in `` will be made available to the statement. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let numbers = 3..=5; +/// +/// let tokens: Tokens<()> = quote! { +/// Your numbers are: $(for n in numbers => $n$[' ']) +/// }; +/// +/// assert_eq!("Your numbers are: 3 4 5", tokens.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// # Joining Loops +/// +/// You can add `join ()` to the end of a repetition. +/// +/// The expression specified in `join ()` is added _between_ each +/// element produced by the loop. +/// +/// > *Note:* The argument to `join` is *whitespace sensitive*, so leading and +/// > trailing is preserved. `join (,)` and `join (, )` would therefore produce +/// > different results. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let numbers = 3..=5; +/// +/// let tokens: Tokens<()> = quote! { +/// Your numbers are: $(for n in numbers join (, ) => $n). +/// }; +/// +/// assert_eq!("Your numbers are: 3, 4, 5.", tokens.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// # Conditionals +/// +/// You can specify a conditional with `$(if => )` where +/// `` is an pattern or expression evaluating to a `bool`, and `` +/// is a quoted expressions. +/// +/// It's also possible to specify a condition with an else branch, by using +/// `$(if { } else { })`. `` is also a quoted +/// expression. +/// +/// ``` +/// use genco::prelude::*; +/// +/// fn greeting(hello: bool, name: &str) -> Tokens<()> { +/// quote!(Custom Greeting: $(if hello { +/// Hello $name +/// } else { +/// Goodbye $name +/// })) +/// } +/// +/// let tokens = greeting(true, "John"); +/// assert_eq!("Custom Greeting: Hello John", tokens.to_string()?); +/// +/// let tokens = greeting(false, "John"); +/// assert_eq!("Custom Greeting: Goodbye John", tokens.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// The `` branch is optional, conditionals which do not have an else +/// branch and evaluated to `false` won't produce any tokens: +/// +/// ``` +/// use genco::prelude::*; +/// +/// fn greeting(hello: bool, name: &str) -> Tokens<()> { +/// quote!(Custom Greeting:$(if hello { +/// $[' ']Hello $name +/// })) +/// } +/// +/// let tokens = greeting(true, "John"); +/// assert_eq!("Custom Greeting: Hello John", tokens.to_string()?); +/// +/// let tokens = greeting(false, "John"); +/// assert_eq!("Custom Greeting:", tokens.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// # Match Statements +/// +/// You can specify a match expression using `$(match { [ => +/// ,]* }`, where `` is an evaluated expression that is match +/// against each subsequent ``. If a pattern matches, the arm with the +/// matching `` block is evaluated. +/// +/// ``` +/// use genco::prelude::*; +/// +/// fn greeting(name: &str) -> Tokens<()> { +/// quote!(Hello $(match name { +/// "John" | "Jane" => $("Random Stranger"), +/// other => $other, +/// })) +/// } +/// +/// let tokens = greeting("John"); +/// assert_eq!("Hello Random Stranger", tokens.to_string()?); +/// +/// let tokens = greeting("Mio"); +/// assert_eq!("Hello Mio", tokens.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +/// If a match arm contains parenthesis (`=> ()`), the expansion will be +/// *whitespace sensitive*. Allowing leading and trailing whitespace to be +/// preserved: +/// +/// ``` +/// use genco::prelude::*; +/// +/// fn greeting(name: &str) -> Tokens<()> { +/// quote!(Hello$(match name { +/// "John" | "Jane" => ( $("Random Stranger")), +/// other => ( $other), +/// })) +/// } +/// +/// let tokens = greeting("John"); +/// assert_eq!("Hello Random Stranger", tokens.to_string()?); +/// +/// let tokens = greeting("Mio"); +/// assert_eq!("Hello Mio", tokens.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +/// The following is an example with more complex matching: +/// +/// ``` +/// use genco::prelude::*; +/// +/// enum Greeting { +/// Named(&'static str), +/// Unknown, +/// } +/// +/// fn greeting(name: Greeting) -> Tokens<()> { +/// quote!(Hello $(match name { +/// Greeting::Named("John") | Greeting::Named("Jane") => $("Random Stranger"), +/// Greeting::Named(other) => $other, +/// Greeting::Unknown => $("Unknown Person"), +/// })) +/// } +/// +/// let tokens = greeting(Greeting::Named("John")); +/// assert_eq!("Hello Random Stranger", tokens.to_string()?); +/// +/// let tokens = greeting(Greeting::Unknown); +/// assert_eq!("Hello Unknown Person", tokens.to_string()?); +/// +/// let tokens = greeting(Greeting::Named("Mio")); +/// assert_eq!("Hello Mio", tokens.to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// # Scopes +/// +/// You can use `$(ref { })` to gain access to the current +/// token stream. This is an alternative to existing control flow operators if +/// you want to run some custom code during evaluation which is otherwise not +/// supported. This is called a *scope*. +/// +/// For a more compact variant you can omit the braces with `$(ref => +/// )`. +/// +/// ``` +/// use genco::prelude::*; +/// +/// fn quote_greeting(surname: &str, lastname: Option<&str>) -> rust::Tokens { +/// quote! { +/// Hello $surname$(ref toks { +/// if let Some(lastname) = lastname { +/// toks.space(); +/// toks.append(lastname); +/// } +/// }) +/// } +/// } +/// +/// assert_eq!("Hello John", quote_greeting("John", None).to_string()?); +/// assert_eq!("Hello John Doe", quote_greeting("John", Some("Doe")).to_string()?); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// ## Whitespace Detection +/// +/// The [quote!] macro has the following rules for dealing with indentation and +/// spacing. +/// +/// **Spaces** — Two tokens that are separated are spaced. Regardless of how +/// many spaces there are between them. This can be controlled manually by +/// inserting the [`$[' ']`][escape] escape sequence in the token stream. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let tokens: rust::Tokens = quote! { +/// fn test() { +/// println!("Hello... "); +/// +/// println!("World!"); +/// } +/// }; +/// +/// assert_eq!( +/// vec![ +/// "fn test() {", +/// " println!(\"Hello... \");", +/// "", +/// " println!(\"World!\");", +/// "}", +/// ], +/// tokens.to_file_vec()?, +/// ); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// **Line breaking** — Line breaks are detected by leaving two empty lines +/// between two tokens. This can be controlled manually by inserting the +/// [`$['\n']`][escape] escape in the token stream. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let tokens: rust::Tokens = quote! { +/// fn test() { +/// println!("Hello... "); +/// +/// +/// +/// println!("World!"); +/// } +/// }; +/// +/// assert_eq!( +/// vec![ +/// "fn test() {", +/// " println!(\"Hello... \");", +/// "", +/// " println!(\"World!\");", +/// "}", +/// ], +/// tokens.to_file_vec()?, +/// ); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +///
+/// +/// **Indentation** — Indentation is determined on a row-by-row basis. If a +/// column is further in than the one on the preceeding row, it is indented *one +/// level* deeper. +/// +/// If a column starts shallower than a preceeding, non-whitespace only row, it +/// will be matched against previously known indentation levels. Failure to +/// match a previously known level is an error. +/// +/// All indentations inserted during the macro will be unrolled at the end of +/// it. So any trailing indentations will be matched by unindentations. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let tokens: rust::Tokens = quote! { +/// fn test() { +/// println!("Hello... "); +/// +/// println!("World!"); +/// } +/// }; +/// +/// assert_eq!( +/// vec![ +/// "fn test() {", +/// " println!(\"Hello... \");", +/// "", +/// " println!(\"World!\");", +/// "}", +/// ], +/// tokens.to_file_vec()?, +/// ); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +/// Example showcasing an indentation mismatch: +/// +/// ```,compile_fail +/// use genco::prelude::*; +/// +/// let tokens: rust::Tokens = quote! { +/// fn test() { +/// println!("Hello... "); +/// +/// println!("World!"); +/// } +/// }; +/// ``` +/// +/// ```text +/// ---- src\lib.rs - (line 150) stdout ---- +/// error: expected 4 less spaces of indentation +/// --> src\lib.rs:157:9 +/// | +/// 10 | println!("World!"); +/// | ^^^^^^^ +/// ``` +/// +/// [escape]: #escape-sequences +pub use genco_macros::quote; + +/// Convenience macro for constructing a [FormatInto] implementation in-place. +/// +/// Constructing [FormatInto] implementation instead of short lived [token +/// streams] can be more beneficial for memory use and performance. +/// +/// [FormatInto]: crate::tokens::FormatInto +/// [token streams]: Tokens +/// +/// # Comparison +/// +/// In the below example, `f1` and `f2` are equivalent. In here [quote_fn!] +/// simply makes it easier to build. +/// +/// ``` +/// use genco::prelude::*; +/// use genco::tokens::from_fn; +/// +/// let f1 = from_fn(move |t| { +/// quote_in!{ *t => +/// println!("Hello World"); +/// } +/// }); +/// +/// let f2 = quote_fn!{ +/// println!("Hello World"); +/// }; +/// +/// let tokens: rust::Tokens = quote!{ +/// $f1 +/// $f2 +/// }; +/// +/// assert_eq!{ +/// vec![ +/// "println!(\"Hello World\");", +/// "println!(\"Hello World\");", +/// ], +/// tokens.to_file_vec()?, +/// }; +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +/// +/// # Examples which borrow +/// +/// ``` +/// use genco::prelude::*; +/// +/// fn greeting(name: &str) -> impl FormatInto + '_ { +/// quote_fn! { +/// println!($[str](Hello $[const](name))) +/// } +/// } +/// +/// fn advanced_greeting<'a>(first: &'a str, last: &'a str) -> impl FormatInto + 'a { +/// quote_fn! { +/// println!($[str](Hello $[const](first) $[const](last))) +/// } +/// } +/// +/// let tokens = quote! { +/// $(greeting("Mio")); +/// $(advanced_greeting("Jane", "Doe")); +/// }; +/// +/// assert_eq!{ +/// vec![ +/// "println!(\"Hello Mio\");", +/// "println!(\"Hello Jane Doe\");", +/// ], +/// tokens.to_file_vec()? +/// }; +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +pub use genco_macros::quote_fn; + +/// Behaves the same as [quote!] while quoting into an existing token stream +/// with ` => `. +/// +/// This macro takes a destination stream followed by an `=>` and the tokens to +/// extend that stream with. +/// +/// Note that the `` arguments must be borrowable. So a mutable +/// reference like `&mut rust::Tokens` will have to be dereferenced when used +/// with this macro. +/// +/// ``` +/// # use genco::prelude::*; +/// +/// # fn generate() -> rust::Tokens { +/// let mut tokens = rust::Tokens::new(); +/// quote_in!(tokens => hello world); +/// # tokens +/// # } +/// +/// fn generate_into(tokens: &mut rust::Tokens) { +/// quote_in! { *tokens => +/// hello... +/// world! +/// }; +/// } +/// ``` +/// +/// # Example +/// +/// ``` +/// use genco::prelude::*; +/// +/// let mut tokens = rust::Tokens::new(); +/// +/// quote_in! { tokens => +/// fn foo() -> u32 { +/// 42 +/// } +/// } +/// ``` +/// +/// # Use with scopes +/// +/// [quote_in!] can be used inside of a [quote!] through [a scope]. +/// +/// ``` +/// use genco::prelude::*; +/// +/// let tokens: rust::Tokens = quote! { +/// fn foo(v: bool) -> u32 { +/// $(ref out { +/// quote_in! { *out => +/// if v { +/// 1 +/// } else { +/// 0 +/// } +/// } +/// }) +/// } +/// }; +/// ``` +/// +/// [a scope]: quote#scopes +pub use genco_macros::quote_in; #[macro_use] mod macros;