Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions mlua_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ license = "MIT"
proc-macro = true

[features]
macros = ["proc-macro-error2", "itertools"]
macros = ["itertools"]

[dependencies]
quote = "1.0"
proc-macro2 = { version = "1.0", features = ["span-locations"] }
proc-macro-error2 = { version = "2.0.1", optional = true }
syn = { version = "2.0", features = ["full"] }
itertools = { version = "0.14", optional = true }
8 changes: 4 additions & 4 deletions mlua_derive/src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ pub(crate) struct Chunk {
}

impl Chunk {
pub(crate) fn new(tokens: TokenStream) -> Self {
let tokens = Tokens::retokenize(tokens);
pub(crate) fn new(tokens: TokenStream) -> Result<Self, TokenStream2> {
let tokens = Tokens::retokenize(tokens)?;

let mut source = String::new();
let mut caps = Captures::new();
Expand All @@ -91,10 +91,10 @@ impl Chunk {
prev_end = Some(t.end());
}

Self {
Ok(Self {
source: source.trim_end().to_string(),
caps,
}
})
}

pub(crate) fn captures(&self) -> &[Capture] {
Expand Down
103 changes: 57 additions & 46 deletions mlua_derive/src/chunk/token.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::cmp::{Eq, PartialEq};
use std::convert::TryFrom;
use std::fmt::{self, Display, Formatter};
use std::vec::IntoIter;

use itertools::Itertools;
use proc_macro::{Delimiter, Span, TokenStream, TokenTree};
use proc_macro2::Span as Span2;
use proc_macro2::{Span as Span2, TokenStream as TokenStream2};
use syn;

#[derive(Clone, Copy, Debug)]
pub(crate) struct Pos {
Expand Down Expand Up @@ -32,19 +33,21 @@ impl Pos {
}
}

fn span_pos(span: &Span) -> (Pos, Pos) {
fn span_pos(span: &Span) -> Result<(Pos, Pos), TokenStream2> {
let span2: Span2 = (*span).into();
let start = span2.start();
let end = span2.end();

// Rust 1.88 stabilized Span APIs, so this branch must be unreachable
if start.line == 0 || end.line == 0 {
proc_macro_error2::abort_call_site!(
"cannot retrieve span location information; mlua requires nightly Rust or stable >= 1.88"
);
return Err(syn::Error::new(
Span2::call_site(),
"cannot retrieve span location information; mlua requires nightly Rust or stable >= 1.88",
)
.to_compile_error());
}

(Pos::new(start.line, start.column), Pos::new(end.line, end.column))
Ok((Pos::new(start.line, start.column), Pos::new(end.line, end.column)))
}

/// Attribute of token.
Expand Down Expand Up @@ -74,33 +77,32 @@ impl PartialEq for Token {
impl Eq for Token {}

impl Token {
fn new(tree: TokenTree) -> Self {
let (start, end) = span_pos(&tree.span());
fn new(tree: TokenTree) -> Result<Self, TokenStream2> {
let (start, end) = span_pos(&tree.span())?;
let source = tree.span().source_text().unwrap_or_else(|| tree.to_string());
Self {
Ok(Self {
source,
start,
end,
tree,
attr: TokenAttr::None,
}
})
}

fn new_delim(source: String, tree: TokenTree, open: bool) -> Self {
let (start, end) = span_pos(&tree.span());
fn new_delim(source: String, tree: TokenTree, open: bool) -> Result<Self, TokenStream2> {
let (start, end) = span_pos(&tree.span())?;
let (start, end) = if open {
(start, start.right())
} else {
(end.left(), end)
};

Self {
Ok(Self {
source,
tree,
start,
end,
attr: TokenAttr::None,
}
})
}

pub(crate) fn tree(&self) -> &TokenTree {
Expand Down Expand Up @@ -133,27 +135,33 @@ impl Token {
pub(crate) struct Tokens(pub(crate) Vec<Token>);

impl Tokens {
pub(crate) fn retokenize(tt: TokenStream) -> Tokens {
Tokens(
tt.into_iter()
.flat_map(Tokens::from)
.batching(|iter| {
// Find variable tokens: `$` + `ident` => `$ident`
let t = iter.next()?;
if t.is("$") {
if let Some(next) = iter.next()
&& matches!(next.tree, TokenTree::Ident(_))
{
Some(next.attr(TokenAttr::Cap))
} else {
proc_macro_error2::abort!(t.tree.span(), "`$` must be followed by an identifier");
}
} else {
Some(t)
}
})
.collect(),
)
pub(crate) fn retokenize(tt: TokenStream) -> Result<Tokens, TokenStream2> {
let mut flat = Vec::new();
for tree in tt.into_iter() {
flat.extend(Tokens::try_from(tree)?.0);
}

let mut tokens = Vec::new();
let mut iter = flat.into_iter();
while let Some(t) = iter.next() {
// Find variable tokens: `$` + `ident` => `$ident`
if t.is("$") {
if let Some(next) = iter.next()
&& matches!(next.tree, TokenTree::Ident(_))
{
tokens.push(next.attr(TokenAttr::Cap));
} else {
return Err(syn::Error::new(
t.tree.span().into(),
"`$` must be followed by an identifier",
)
.to_compile_error());
}
} else {
tokens.push(t);
}
}
Ok(Tokens(tokens))
}
}

Expand All @@ -166,8 +174,10 @@ impl IntoIterator for Tokens {
}
}

impl From<TokenTree> for Tokens {
fn from(tt: TokenTree) -> Self {
impl TryFrom<TokenTree> for Tokens {
type Error = TokenStream2;

fn try_from(tt: TokenTree) -> Result<Self, TokenStream2> {
let tts = match tt.clone() {
TokenTree::Group(g) => {
let (b, e) = match g.delimiter() {
Expand All @@ -178,15 +188,16 @@ impl From<TokenTree> for Tokens {
};
let (b, e) = (b.into(), e.into());

vec![Token::new_delim(b, tt.clone(), true)]
.into_iter()
.chain(g.stream().into_iter().flat_map(Tokens::from))
.chain(vec![Token::new_delim(e, tt, false)])
.collect()
let mut result = vec![Token::new_delim(b, tt.clone(), true)?];
for inner in g.stream().into_iter() {
result.extend(Tokens::try_from(inner)?.0);
}
result.push(Token::new_delim(e, tt, false)?);
result
}
_ => vec![Token::new(tt)],
_ => vec![Token::new(tt)?],
};
Tokens(tts)
Ok(Tokens(tts))
}
}

Expand Down
8 changes: 5 additions & 3 deletions mlua_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use proc_macro::TokenStream;
mod module;

#[cfg(feature = "macros")]
use {crate::chunk::Chunk, proc_macro_error2::proc_macro_error};
use crate::chunk::Chunk;

#[cfg(feature = "macros")]
macro_rules! try_compile {
Expand All @@ -22,9 +22,11 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {

#[cfg(feature = "macros")]
#[proc_macro]
#[proc_macro_error]
pub fn chunk(input: TokenStream) -> TokenStream {
Chunk::new(input).expand().into()
match Chunk::new(input) {
Ok(chunk) => chunk.expand().into(),
Err(err) => err.into(),
}
}

#[cfg(feature = "macros")]
Expand Down
Loading