aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2021-04-06 13:33:32 -0600
committerMelody Horn <melody@boringcactus.com>2021-04-06 13:33:32 -0600
commit1602ad07efa6d3c4170928537843a0d61a5cd5e7 (patch)
tree94e5f02d0fb49eca6dab1060fa6abc099be3bf48
parentdb61eecb92347f963edf9283746cdde4d88fb67b (diff)
downloadmakers-1602ad07efa6d3c4170928537843a0d61a5cd5e7.tar.gz
makers-1602ad07efa6d3c4170928537843a0d61a5cd5e7.zip
fix balanced parens in function arguments
-rw-r--r--src/makefile/token.rs91
1 files changed, 54 insertions, 37 deletions
diff --git a/src/makefile/token.rs b/src/makefile/token.rs
index a79598a..ca50fe7 100644
--- a/src/makefile/token.rs
+++ b/src/makefile/token.rs
@@ -9,7 +9,7 @@ use nom::{
combinator::{all_consuming, map, opt, verify},
error::{context, convert_error, ContextError, ParseError, VerboseError},
multi::fold_many1,
- sequence::{delimited, pair, preceded, separated_pair},
+ sequence::{delimited, pair, preceded, separated_pair, tuple},
Finish, IResult,
};
#[cfg(feature = "full")]
@@ -105,7 +105,15 @@ impl TokenString {
}
pub fn extend(&mut self, other: Self) {
- self.0.extend(other.0);
+ let mut incoming = other.0.into_iter().peekable();
+ if let Some(Token::Text(text)) = self.0.last_mut() {
+ while let Some(Token::Text(incoming_text)) =
+ incoming.next_if(|x| matches!(x, Token::Text(_)))
+ {
+ text.push_str(&incoming_text);
+ }
+ }
+ self.0.extend(incoming);
}
pub fn trim_start(&mut self) {
@@ -192,6 +200,13 @@ impl Delimiter {
}
}
+ fn start_char(&self) -> char {
+ match self {
+ Self::Parens => '(',
+ Self::Braces => '{',
+ }
+ }
+
fn end(&self) -> &'static str {
match self {
Self::Parens => ")",
@@ -210,7 +225,7 @@ fn macro_function_name<'a, E: Err<'a>>(
fn macro_expansion_body<'a, E: Err<'a>>(
end: char,
delim: Delimiter,
-) -> impl FnMut(&'a str) -> IResult<&'a str, Token, E> + 'a {
+) -> impl FnMut(&'a str) -> IResult<&'a str, TokenString, E> + 'a {
let subst = preceded(
tag(":"),
separated_pair(
@@ -223,7 +238,7 @@ fn macro_expansion_body<'a, E: Err<'a>>(
"macro_expansion_body",
map(
pair(macro_function_name(end, delim), opt(subst)),
- |(name, replacement)| Token::MacroExpansion { name, replacement },
+ |(name, replacement)| TokenString::just(Token::MacroExpansion { name, replacement }),
),
)
}
@@ -232,7 +247,7 @@ fn macro_expansion_body<'a, E: Err<'a>>(
fn function_call_body<'a, E: Err<'a>>(
end: char,
delim: Delimiter,
-) -> impl FnMut(&'a str) -> IResult<&'a str, Token, E> {
+) -> impl FnMut(&'a str) -> IResult<&'a str, TokenString, E> {
context(
"function_call_body",
map(
@@ -244,7 +259,7 @@ fn function_call_body<'a, E: Err<'a>>(
tokens_but_not(vec![',', end], delim),
),
),
- |(name, args)| Token::FunctionCall { name, args },
+ |(name, args)| TokenString::just(Token::FunctionCall { name, args }),
),
)
}
@@ -253,7 +268,7 @@ fn function_call_body<'a, E: Err<'a>>(
fn macro_body<'a, E: Err<'a>>(
end: char,
context: Delimiter,
-) -> impl FnMut(&'a str) -> IResult<&'a str, Token, E> {
+) -> impl FnMut(&'a str) -> IResult<&'a str, TokenString, E> {
alt((
function_call_body(end, context),
macro_expansion_body(end, context),
@@ -261,28 +276,27 @@ fn macro_body<'a, E: Err<'a>>(
}
#[cfg(not(feature = "full"))]
-fn macro_body<'a, E: Err<'a>>(end: char) -> impl FnMut(&'a str) -> IResult<&'a str, Token, E> {
+fn macro_body<'a, E: Err<'a>>(
+ end: char,
+) -> impl FnMut(&'a str) -> IResult<&'a str, TokenString, E> {
macro_expansion_body(end)
}
-fn parens_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, Token, E> {
+fn parens_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, TokenString, E> {
delimited(tag("$("), macro_body(')', Delimiter::Parens), tag(")"))(input)
}
-fn braces_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, Token, E> {
- delimited(tag("${"), macro_body('}', Delimiter::Parens), tag("}"))(input)
+fn braces_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, TokenString, E> {
+ delimited(tag("${"), macro_body('}', Delimiter::Braces), tag("}"))(input)
}
-fn tiny_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, Token, E> {
+fn tiny_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, TokenString, E> {
let raw = preceded(tag("$"), verify(anychar, |&c| c != '(' && c != '{'));
map(raw, |c| {
if c == '$' {
- Token::Text("$".into())
+ TokenString::text("$")
} else {
- Token::MacroExpansion {
- name: TokenString::text(c),
- replacement: None,
- }
+ TokenString::r#macro(c)
}
})(input)
}
@@ -290,14 +304,11 @@ fn tiny_macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, Toke
fn macro_expansion<'a, E: Err<'a>>(input: &'a str) -> IResult<&'a str, TokenString, E> {
context(
"macro_expansion",
- map(
- alt((
- tiny_macro_expansion,
- parens_macro_expansion,
- braces_macro_expansion,
- )),
- TokenString::just,
- ),
+ alt((
+ tiny_macro_expansion,
+ parens_macro_expansion,
+ braces_macro_expansion,
+ )),
)(input)
}
@@ -314,10 +325,18 @@ fn nested_delimiters<'a, E: Err<'a>>(
ends: Vec<char>,
context: Delimiter,
) -> impl FnMut(&'a str) -> IResult<&'a str, TokenString, E> {
- delimited(
- tag(context.start()),
- tokens_but_not(ends, context),
- tag(context.end()),
+ map(
+ tuple((
+ tag(context.start()),
+ move |x| tokens_but_not(ends.clone(), context)(x),
+ tag(context.end()),
+ )),
+ |(left, center, right)| {
+ let mut tokens = TokenString::text(left);
+ tokens.extend(center);
+ tokens.extend(TokenString::text(right));
+ tokens
+ },
)
}
@@ -325,10 +344,12 @@ fn single_token_but_not<'a, E: Err<'a>>(
ends: Vec<char>,
context: Delimiter,
) -> impl FnMut(&'a str) -> IResult<&'a str, TokenString, E> {
+ let mut tbn_ends = ends.clone();
+ tbn_ends.push(context.start_char());
alt((
- text_but_not(ends.clone()),
+ text_but_not(tbn_ends),
macro_expansion,
- nested_delimiters(ends, context),
+ nested_delimiters(ends.clone(), context),
))
}
@@ -461,11 +482,7 @@ mod test {
let tokens = tokenize(text)?;
assert_eq!(
tokens,
- TokenString(vec![
- token_text("This costs "),
- token_text("$"),
- token_text("2 to run, which isn't ideal"),
- ])
+ TokenString(vec![token_text("This costs $2 to run, which isn't ideal"),])
);
Ok(())
}
@@ -573,7 +590,7 @@ mod test {
tokens,
TokenString(vec![Token::FunctionCall {
name: TokenString::text("shell"),
- args: vec![TokenString::text("echo (hi) (bro) (yeet)")],
+ args: vec![TokenString::text("echo (hi) (bro) yeet")],
}])
);
Ok(())