Skip to content

Commit 9eb64b2

Browse files
committed
Change the parse_from to always accept a nom expression for parsing.
We'll add a separate macro for matching strings.
1 parent 58415ed commit 9eb64b2

File tree

7 files changed

+62
-64
lines changed

7 files changed

+62
-64
lines changed

Cargo.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ path = "examples/basic_struct_with_members.rs"
2929
name = "use_matching_string"
3030
path = "examples/use_matching_string.rs"
3131

32-
[[example]]
33-
name = "use_prefix_and_suffix"
34-
path = "examples/use_prefix_and_suffix.rs"
35-
3632
[dependencies]
3733
quote = "1.0"
3834
proc-macro2 = "1.0"

examples/basic_struct_with_members.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use nom_parse_macros::parse_from;
22
use nom_parse_trait::ParseFrom;
33

4-
#[parse_from(split = tuple(space0, ",", space0))]
4+
#[parse_from(separated_pair({}, tuple(space0, ",", space0), {}))]
55
#[derive(Debug, PartialEq)]
66
struct NumberPair {
77
x: u32,

examples/use_prefix_and_suffix.rs

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/fields.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use quote::quote;
22
use proc_macro2::{Ident, Span};
3-
use syn::{Fields, FieldsNamed, FieldsUnnamed};
3+
use syn::{Fields, FieldsNamed, FieldsUnnamed, Type};
44

55
pub enum Field {
66
Default {
77
name: Ident,
8-
ty: syn::Type,
8+
ty: Type,
99
}
1010
}
1111

@@ -50,4 +50,10 @@ impl Field {
5050
Field::Default { name, .. } => name,
5151
}
5252
}
53+
54+
pub fn get_type(&self) -> &Type {
55+
match self {
56+
Field::Default { ty, .. } => ty,
57+
}
58+
}
5359
}

src/lib.rs

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,36 @@ mod settings;
66
use proc_macro::TokenStream;
77

88
use crate::fields::parse_fields;
9-
use crate::settings::ParseSettings;
9+
use crate::nom_packages::update_nom_expression;
1010
use quote::quote;
11-
use syn::ItemStruct;
11+
use syn::{parse_macro_input, Expr, ItemStruct, LitStr};
1212

1313
#[proc_macro_attribute]
1414
pub fn parse_from(attrs: TokenStream, object: TokenStream) -> TokenStream {
15-
let parse_settings = match ParseSettings::from(attrs.into()) {
16-
Ok(settings) => settings,
17-
Err(e) => return e.to_compile_error().into(),
18-
};
15+
let mut expression = parse_macro_input! { attrs as Expr };
16+
if let Err(e) = update_nom_expression(&mut expression) {
17+
return e.to_compile_error().into();
18+
}
1919

20-
let object = syn::parse_macro_input!(object as ItemStruct);
20+
let object = parse_macro_input!(object as ItemStruct);
2121
let fields = parse_fields(&object.fields);
2222
let name = object.ident.clone();
2323

24-
parse_settings
25-
.generate_parse_expressions(&fields)
26-
.map(|expressions| {
27-
let names: Vec<_> = fields.iter().map(|field| field.get_name()).collect();
24+
let names: Vec<_> = fields.iter().map(|field| field.get_name()).collect();
2825

29-
let tokens = quote! {
30-
#object
26+
let tokens = quote! {
27+
#object
3128

32-
impl nom_parse_trait::ParseFrom<&str> for #name {
33-
fn parse(input: &str) -> nom::IResult<&str, Self> {
34-
use nom::Parser;
29+
impl nom_parse_trait::ParseFrom<&str> for #name {
30+
fn parse(input: &str) -> nom::IResult<&str, Self> {
31+
use nom::*;
3532

36-
let mut input = input;
37-
#(#expressions)*
38-
Ok((input, Self { #(#names),* }))
39-
}
40-
}
41-
};
33+
let mut input = input;
34+
let (input, (#(#names),*)) = #expression.parse(input)?;
35+
Ok((input, Self { #(#names),* }))
36+
}
37+
}
38+
};
4239

43-
tokens.into()
44-
})
45-
.unwrap_or_else(|e| e.to_compile_error().into())
40+
tokens.into()
4641
}

src/nom_packages.rs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use syn::punctuated::Punctuated;
55
use syn::{
66
parse, parse_str, Expr, ExprArray, ExprCall, ExprLit, ExprPath, ExprTuple, Lit, Path, Result,
77
};
8+
use syn::spanned::Spanned;
89

910
const NOM_FUNCTIONS: phf::Map<&'static str, (&'static str, &'static [bool])> = phf::phf_map! {
1011
// From the nom::branch module
@@ -99,24 +100,35 @@ const NOM_FUNCTIONS: phf::Map<&'static str, (&'static str, &'static [bool])> = p
99100
"tuple" => ("nom::sequence::tuple", &[]), // Special handling for tuples
100101
};
101102

102-
pub fn handle_nom_parse_expression(expr: &mut Expr) -> Result<()> {
103+
pub fn update_nom_expression(expr: &mut Expr) -> Result<()> {
103104
match expr {
105+
Expr::Block(block_expr) => {
106+
if block_expr.block.stmts.is_empty() {
107+
*expr = parse::<Expr>(quote_spanned! { block_expr.span() => nom_parse_trait::ParseFrom::parse }.into())?;
108+
Ok(())
109+
} else {
110+
Err(syn::Error::new_spanned(
111+
block_expr,
112+
"Only supporting building nom parsers from function calls and string literals",
113+
))
114+
}
115+
},
104116
Expr::Call(call) => parse_call(call),
105117
Expr::Lit(lit_expr) => match &lit_expr.lit {
106118
Lit::Str(value) => {
107-
*expr = generate_tag_expression(value.value().as_bytes(), value.span());
119+
*expr = generate_match_expression(value.value().as_bytes(), value.span());
108120
Ok(())
109121
}
110122
Lit::ByteStr(value) => {
111-
*expr = generate_tag_expression(&value.value(), value.span());
123+
*expr = generate_match_expression(&value.value(), value.span());
112124
Ok(())
113125
}
114126
Lit::Byte(value) => {
115-
*expr = generate_tag_expression(&[value.value()], value.span());
127+
*expr = generate_match_expression(&[value.value()], value.span());
116128
Ok(())
117129
}
118130
Lit::Char(value) => {
119-
*expr = generate_tag_expression(value.value().to_string().as_bytes(), value.span());
131+
*expr = generate_match_expression(value.value().to_string().as_bytes(), value.span());
120132
Ok(())
121133
}
122134
_ => Err(syn::Error::new_spanned(
@@ -126,9 +138,14 @@ pub fn handle_nom_parse_expression(expr: &mut Expr) -> Result<()> {
126138
},
127139
Expr::Path(ExprPath { path, .. }) => parse_path(path),
128140
Expr::Tuple(ExprTuple { elems, .. }) => {
129-
// Tuples are assumed to be all parsers
130-
for elem in elems.iter_mut() {
131-
handle_nom_parse_expression(elem)?;
141+
if elems.is_empty() {
142+
// An empty tuple is used as a shortcut for the ParseFrom parser
143+
*expr = parse::<Expr>(quote_spanned! { elems.span() => nom_parse_trait::ParseFrom::parse }.into())?;
144+
} else {
145+
// Tuples are assumed to be all parsers
146+
for elem in elems.iter_mut() {
147+
update_nom_expression(elem)?;
148+
}
132149
}
133150
Ok(())
134151
}
@@ -161,7 +178,7 @@ fn parse_call(call: &mut ExprCall) -> Result<()> {
161178
}
162179

163180
for arg in call.args.iter_mut() {
164-
handle_nom_parse_expression(arg)?;
181+
update_nom_expression(arg)?;
165182
}
166183
// Nom functions without parameters should not be called, but referenced directly
167184
} else if parameters.len() == 0 {
@@ -183,7 +200,7 @@ fn parse_call(call: &mut ExprCall) -> Result<()> {
183200
} else {
184201
for (arg, &is_parser) in call.args.iter_mut().zip(parameters) {
185202
if is_parser {
186-
handle_nom_parse_expression(arg)?;
203+
update_nom_expression(arg)?;
187204
}
188205
}
189206
}
@@ -221,7 +238,7 @@ pub fn parse_path(path_expr: &mut Path) -> Result<()> {
221238
Ok(())
222239
}
223240

224-
pub fn generate_tag_expression(value: &[u8], span: Span) -> Expr {
241+
pub fn generate_match_expression(value: &[u8], span: Span) -> Expr {
225242
let mut array = parse_str::<ExprArray>(format!("{:?}", value).as_str()).unwrap();
226243
array.bracket_token.span = span.into_spans();
227244
array.elems.iter_mut().for_each(|elem| {

src/settings.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::fields::Field;
2-
use crate::nom_packages::{generate_tag_expression, handle_nom_parse_expression};
2+
use crate::nom_packages::{generate_match_expression, update_nom_expression};
33
use itertools::Itertools;
44
use proc_macro::Span;
55
use proc_macro2::TokenStream;
@@ -30,7 +30,7 @@ impl ParseSettings {
3030
suffix,
3131
} => {
3232
let mut split = split.clone();
33-
handle_nom_parse_expression(&mut split)?;
33+
update_nom_expression(&mut split)?;
3434

3535
let mut expressions: Vec<_> = Itertools::intersperse(
3636
fields
@@ -42,7 +42,7 @@ impl ParseSettings {
4242

4343
if let Some(prefix) = prefix {
4444
let mut prefix = prefix.clone();
45-
handle_nom_parse_expression(&mut prefix)?;
45+
update_nom_expression(&mut prefix)?;
4646
expressions.insert(
4747
0,
4848
quote_spanned! { prefix.span() => let (input, _) = #prefix.parse(input)?; },
@@ -51,7 +51,7 @@ impl ParseSettings {
5151

5252
if let Some(suffix) = suffix {
5353
let mut suffix = suffix.clone();
54-
handle_nom_parse_expression(&mut suffix)?;
54+
update_nom_expression(&mut suffix)?;
5555
expressions.push(
5656
quote_spanned! { suffix.span() => let (input, _) = #suffix.parse(input)?; },
5757
);
@@ -68,7 +68,7 @@ impl ParseSettings {
6868
let parts: Vec<_> = value
6969
.split("{}")
7070
.map(|part| {
71-
let expr = generate_tag_expression(part.as_bytes(), literal.span());
71+
let expr = generate_match_expression(part.as_bytes(), literal.span());
7272
quote_spanned! { literal.span() => let (input, _) = #expr.parse(input)?; }
7373
})
7474
.collect();

0 commit comments

Comments
 (0)