aboutsummaryrefslogtreecommitdiff
path: root/src/makefile
diff options
context:
space:
mode:
Diffstat (limited to 'src/makefile')
-rw-r--r--src/makefile/mod.rs5
-rw-r--r--src/makefile/token.rs125
2 files changed, 92 insertions, 38 deletions
diff --git a/src/makefile/mod.rs b/src/makefile/mod.rs
index 00c5051..c118eb9 100644
--- a/src/makefile/mod.rs
+++ b/src/makefile/mod.rs
@@ -714,10 +714,13 @@ mod test {
#[test]
fn basic_conditionals() -> R {
let file = "
-ifeq (1,1)
+ifneq (,$(foo bar, $(baz)))
+else ifeq (1,1)
worked = yes
else ifeq (2,2)
worked = no
+else
+worked = perhaps
endif
";
let args = Args::empty();
diff --git a/src/makefile/token.rs b/src/makefile/token.rs
index 076ac60..0ee3e4c 100644
--- a/src/makefile/token.rs
+++ b/src/makefile/token.rs
@@ -7,11 +7,15 @@ use nom::{
bytes::complete::{tag, take_till1, take_while1},
character::complete::{anychar, space1},
combinator::{all_consuming, map, opt, verify},
+ error::{context, convert_error, ContextError, ParseError, VerboseError},
multi::{many1, separated_list1},
sequence::{delimited, pair, preceded, separated_pair},
Finish, IResult,
};
+trait Err<'a>: 'a + ParseError<&'a str> + ContextError<&'a str> {}
+impl<'a, T: 'a + ParseError<&'a str> + ContextError<&'a str>> Err<'a> for T {}
+
#[allow(clippy::module_name_repetitions)]
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct TokenString(Vec<Token>);
@@ -150,13 +154,15 @@ impl fmt::Display for Token {
}
}
-fn macro_function_name(input: &str) -> IResult<&str, &str> {
+fn macro_function_name<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, &'a str, E> {
// POSIX says "periods, underscores, digits, and alphabetics from the portable character set"
// one GNUism is a function with a - in the name
take_while1(|c: char| c == '.' || c == '_' || c.is_alphanumeric() || c == '-')(input)
}
-fn macro_expansion_body<'a>(end: char) -> impl FnMut(&'a str) -> IResult<&'a str, Token> + 'a {
+fn macro_expansion_body<'a, E: Err<'a>>(
+ end: char,
+) -> impl FnMut(&'a str) -> IResult<&'a str, Token, E> + 'a {
let subst = preceded(
tag(":"),
separated_pair(
@@ -165,30 +171,38 @@ fn macro_expansion_body<'a>(end: char) -> impl FnMut(&'a str) -> IResult<&'a str
tokens_but_not(vec![end]),
),
);
- map(
- pair(macro_function_name, opt(subst)),
- |(name, replacement)| Token::MacroExpansion {
- name: name.into(),
- replacement,
- },
+ context(
+ "macro_expansion_body",
+ map(
+ pair(macro_function_name, opt(subst)),
+ |(name, replacement)| Token::MacroExpansion {
+ name: name.into(),
+ replacement,
+ },
+ ),
)
}
-fn function_call_body<'a>(end: char) -> impl FnMut(&'a str) -> IResult<&'a str, Token> {
- map(
- separated_pair(
- macro_function_name,
- space1,
- separated_list1(tag(","), tokens_but_not(vec![',', end])),
+fn function_call_body<'a, E: Err<'a>>(
+ end: char,
+) -> impl FnMut(&'a str) -> IResult<&'a str, Token, E> {
+ context(
+ "function_call_body",
+ map(
+ separated_pair(
+ macro_function_name,
+ space1,
+ separated_list1(tag(","), tokens_but_not(vec![',', end])),
+ ),
+ |(name, args)| Token::FunctionCall {
+ name: name.into(),
+ args,
+ },
),
- |(name, args)| Token::FunctionCall {
- name: name.into(),
- args,
- },
)
}
-fn parens_macro_expansion(input: &str) -> IResult<&str, Token> {
+fn parens_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, Token, E> {
delimited(
tag("$("),
alt((macro_expansion_body(')'), function_call_body(')'))),
@@ -196,15 +210,15 @@ fn parens_macro_expansion(input: &str) -> IResult<&str, Token> {
)(input)
}
-fn braces_macro_expansion(input: &str) -> IResult<&str, Token> {
+fn braces_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, Token, E> {
delimited(
tag("${"),
- alt((macro_expansion_body('}'), function_call_body(')'))),
+ alt((macro_expansion_body('}'), function_call_body('}'))),
tag("}"),
)(input)
}
-fn tiny_macro_expansion(input: &str) -> IResult<&str, Token> {
+fn tiny_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, Token, E> {
let raw = preceded(tag("$"), verify(anychar, |&c| c != '(' && c != '{'));
map(raw, |c| {
if c == '$' {
@@ -218,44 +232,56 @@ fn tiny_macro_expansion(input: &str) -> IResult<&str, Token> {
})(input)
}
-fn macro_expansion(input: &str) -> IResult<&str, Token> {
- alt((
- tiny_macro_expansion,
- parens_macro_expansion,
- braces_macro_expansion,
- ))(input)
+fn macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, Token, E> {
+ context(
+ "macro_expansion",
+ alt((
+ tiny_macro_expansion,
+ parens_macro_expansion,
+ braces_macro_expansion,
+ )),
+ )(input)
}
-fn text_but_not<'a>(ends: Vec<char>) -> impl FnMut(&'a str) -> IResult<&'a str, Token> {
+fn text_but_not<'a, E: Err<'a>>(
+ ends: Vec<char>,
+) -> impl FnMut(&'a str) -> IResult<&'a str, Token, E> {
map(
take_till1(move |c| c == '$' || ends.contains(&c)),
|x: &str| Token::Text(x.into()),
)
}
-fn single_token_but_not<'a>(ends: Vec<char>) -> impl FnMut(&'a str) -> IResult<&'a str, Token> {
+fn single_token_but_not<'a, E: Err<'a>>(
+ ends: Vec<char>,
+) -> impl FnMut(&'a str) -> IResult<&'a str, Token, E> {
alt((text_but_not(ends), macro_expansion))
}
-fn empty_tokens(input: &str) -> IResult<&str, TokenString> {
- map(tag(""), |_| TokenString(vec![Token::Text(String::new())]))(input)
+fn empty_tokens<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, TokenString, E> {
+ context(
+ "empty_tokens",
+ map(tag(""), |_| TokenString(vec![Token::Text(String::new())])),
+ )(input)
}
-fn tokens_but_not<'a>(ends: Vec<char>) -> impl FnMut(&'a str) -> IResult<&'a str, TokenString> {
+fn tokens_but_not<'a, E: Err<'a>>(
+ ends: Vec<char>,
+) -> impl FnMut(&'a str) -> IResult<&'a str, TokenString, E> {
alt((
map(many1(single_token_but_not(ends)), TokenString),
empty_tokens,
))
}
-fn full_text_tokens(input: &str) -> IResult<&str, TokenString> {
+fn full_text_tokens<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, TokenString, E> {
all_consuming(tokens_but_not(vec![]))(input)
}
pub fn tokenize(input: &str) -> eyre::Result<TokenString> {
let (_, result) = full_text_tokens(input)
.finish()
- .map_err(|err| eyre::eyre!(err.to_string()))
+ .map_err(|err: VerboseError<&str>| eyre::eyre!(convert_error(input, err)))
.with_context(|| format!("couldn't parse {:?}", input))?;
Ok(result)
}
@@ -270,9 +296,9 @@ impl FromStr for TokenString {
#[cfg(test)]
mod test {
- use super::{tokenize, Token, TokenString};
+ use super::*;
- type R = anyhow::Result<()>;
+ type R = eyre::Result<()>;
impl From<Vec<Token>> for TokenString {
fn from(x: Vec<Token>) -> Self {
@@ -302,6 +328,13 @@ mod test {
}
}
+ fn token_function_call(name: impl Into<String>, args: Vec<impl Into<TokenString>>) -> Token {
+ Token::FunctionCall {
+ name: name.into(),
+ args: args.into_iter().map(|x| x.into()).collect(),
+ }
+ }
+
#[test]
fn no_macros() -> R {
let text = "This is an example sentence! There aren't macros in it at all!";
@@ -396,4 +429,22 @@ mod test {
);
Ok(())
}
+
+ #[test]
+ fn function_hell() -> R {
+ dbg!(alt::<_, _, VerboseError<_>, _>((
+ macro_expansion_body(')'),
+ function_call_body(')')
+ ))("$(foo bar,$(baz))"));
+ let text = "$(foo bar, $(baz))";
+ let tokens = tokenize(text)?;
+ assert_eq!(
+ tokens,
+ TokenString(vec![token_function_call(
+ "foo",
+ vec![TokenString::text("bar"), TokenString::r#macro("baz")]
+ )])
+ );
+ Ok(())
+ }
}