From 1602ad07efa6d3c4170928537843a0d61a5cd5e7 Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Tue, 6 Apr 2021 13:33:32 -0600 Subject: fix balanced parens in function arguments --- src/makefile/token.rs | 91 ++++++++++++++++++++++++++++++--------------------- 1 file 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, 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, 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(()) -- cgit v1.2.3