Add Multi-Invokation 👨‍👩‍👧‍👧

This commit is contained in:
Martin Haug 2020-08-27 17:50:57 +02:00
parent 141d69cb60
commit 3de20f8d38
2 changed files with 87 additions and 20 deletions

View File

@ -67,7 +67,7 @@ impl Parser<'_> {
} }
Token::LeftBracket => { Token::LeftBracket => {
self.parse_bracket_call().map(|c| SyntaxNode::Call(c)) self.parse_bracket_call(false).map(|c| SyntaxNode::Call(c))
} }
Token::Star => self.with_span(SyntaxNode::ToggleBolder), Token::Star => self.with_span(SyntaxNode::ToggleBolder),
@ -111,11 +111,15 @@ impl Parser<'_> {
// Function calls. // Function calls.
impl Parser<'_> { impl Parser<'_> {
fn parse_bracket_call(&mut self) -> Spanned<CallExpr> { fn parse_bracket_call(&mut self, chained: bool) -> Spanned<CallExpr> {
let before_bracket = self.pos();
if !chained {
self.start_group(Delimiter::Bracket); self.start_group(Delimiter::Bracket);
self.tokens.push_mode(TokenMode::Header); self.tokens.push_mode(TokenMode::Header);
}
let after_bracket = self.pos(); let after_bracket = self.pos();
self.start_group(Delimiter::Subheader);
self.skip_white(); self.skip_white();
let name = self.parse_ident().unwrap_or_else(|| { let name = self.parse_ident().unwrap_or_else(|| {
self.expected_found_or_at("function name", after_bracket); self.expected_found_or_at("function name", after_bracket);
@ -123,8 +127,12 @@ impl Parser<'_> {
}); });
self.skip_white(); self.skip_white();
let mut last_was_chain = false;
let mut args = match self.eatv() { let mut args = match self.eatv() {
Some(Token::Colon) => self.parse_table_contents().0, Some(Token::Colon) => {
self.parse_table_contents().0
},
Some(_) => { Some(_) => {
self.expected_at("colon", name.span.end); self.expected_at("colon", name.span.end);
while self.eat().is_some() {} while self.eat().is_some() {}
@ -133,10 +141,30 @@ impl Parser<'_> {
None => TableExpr::new(), None => TableExpr::new(),
}; };
self.tokens.pop_mode(); self.end_group();
let mut span = self.end_group(); self.skip_white();
if self.peek().is_some() {
last_was_chain = true;
let item = self.parse_bracket_call(true);
let span = item.span;
let t = vec![item.map(|f| SyntaxNode::Call(f))];
args.push(SpannedEntry::val(Spanned::new(Expr::Tree(t), span)));
}
if self.check(Token::LeftBracket) { let end = if !last_was_chain {
self.tokens.pop_mode();
self.end_group().end
} else {
args.last()
.expect("last_was_chain set, call expected")
.1.val.span.end
};
let start = if chained { after_bracket } else { before_bracket };
let mut span = Span::new(start, end);
if self.check(Token::LeftBracket) && !last_was_chain {
self.start_group(Delimiter::Bracket); self.start_group(Delimiter::Bracket);
self.tokens.push_mode(TokenMode::Body); self.tokens.push_mode(TokenMode::Body);
@ -358,7 +386,7 @@ impl Parser<'_> {
// This is a bracketed function call. // This is a bracketed function call.
Token::LeftBracket => { Token::LeftBracket => {
let call = self.parse_bracket_call(); let call = self.parse_bracket_call(false);
let tree = vec![call.map(|c| SyntaxNode::Call(c))]; let tree = vec![call.map(|c| SyntaxNode::Call(c))];
Spanned::new(Expr::Tree(tree), span) Spanned::new(Expr::Tree(tree), span)
} }
@ -415,12 +443,18 @@ impl Parser<'_> {
impl<'s> Parser<'s> { impl<'s> Parser<'s> {
fn start_group(&mut self, delimiter: Delimiter) { fn start_group(&mut self, delimiter: Delimiter) {
let start = self.pos(); let start = self.pos();
self.assert(delimiter.start()); if let Some(start_token) = delimiter.start() {
self.assert(start_token);
}
self.delimiters.push((start, delimiter.end())); self.delimiters.push((start, delimiter.end()));
} }
fn end_group(&mut self) -> Span { fn end_group(&mut self) -> Span {
if self.delimiters.last()
.expect("group was not started").1 != Token::FunctionLink {
assert_eq!(self.peek(), None, "unfinished group"); assert_eq!(self.peek(), None, "unfinished group");
}
let (start, end_token) = self.delimiters.pop() let (start, end_token) = self.delimiters.pop()
.expect("group was not started"); .expect("group was not started");
@ -431,10 +465,12 @@ impl<'s> Parser<'s> {
} }
_ => { _ => {
let end = self.pos(); let end = self.pos();
if end_token != Token::FunctionLink {
error!( error!(
@self.feedback, Span::at(end), @self.feedback, Span::at(end),
"expected {}", end_token.name(), "expected {}", end_token.name(),
); );
}
Span::new(start, end) Span::new(start, end)
} }
} }
@ -471,6 +507,7 @@ impl<'s> Parser<'s> {
} }
} }
/// Checks if the next token is of some kind
fn check(&mut self, token: Token<'_>) -> bool { fn check(&mut self, token: Token<'_>) -> bool {
self.peekv() == Some(token) self.peekv() == Some(token)
} }
@ -517,21 +554,24 @@ enum Delimiter {
Paren, Paren,
Bracket, Bracket,
Brace, Brace,
Subheader,
} }
impl Delimiter { impl Delimiter {
fn is_delimiter(token: Token<'_>) -> bool { fn is_delimiter(token: Token<'_>) -> bool {
matches!( matches!(
token, token,
Token::RightParen | Token::RightBracket | Token::RightBrace Token::RightParen | Token::RightBracket
| Token::RightBrace | Token::FunctionLink
) )
} }
fn start(self) -> Token<'static> { fn start(self) -> Option<Token<'static>> {
match self { match self {
Self::Paren => Token::LeftParen, Self::Paren => Some(Token::LeftParen),
Self::Bracket => Token::LeftBracket, Self::Bracket => Some(Token::LeftBracket),
Self::Brace => Token::LeftBrace, Self::Brace => Some(Token::LeftBrace),
Self::Subheader => None,
} }
} }
@ -540,6 +580,7 @@ impl Delimiter {
Self::Paren => Token::RightParen, Self::Paren => Token::RightParen,
Self::Bracket => Token::RightBracket, Self::Bracket => Token::RightBracket,
Self::Brace => Token::RightBrace, Self::Brace => Token::RightBrace,
Self::Subheader => Token::FunctionLink,
} }
} }
} }
@ -837,6 +878,18 @@ mod tests {
e!("[ 🌎]" => s(0,3, 0,4, "expected function name, found invalid token")); e!("[ 🌎]" => s(0,3, 0,4, "expected function name, found invalid token"));
} }
#[test]
fn test_parse_subgroups() {
// Things the parser has to make sense of
t!("[hi: (5.0, 2.1 >> you]" => P![F!("hi"; Table![Num(5.0), Num(2.1)], Tree![F!("you")])]);
t!("[bold: 400, >> emph >> sub: 1cm]" => P![F!("bold"; Num(400.0), Tree![F!("emph"; Tree!(F!("sub"; Len(Length::cm(1.0)))))])]);
t!("[box >> pad: 1pt][Hi]" => P![F!("box"; Tree![F!("pad"; Len(Length::pt(1.0)), Tree!(P![T("Hi")]))])]);
// Errors for unclosed / empty predecessor groups
e!("[hi: (5.0, 2.1 >> you]" => s(0, 15, 0, 15, "expected closing paren"));
e!("[>> abc]" => s(0, 1, 0, 1, "expected function name"));
}
#[test] #[test]
fn test_parse_colon_starting_func_args() { fn test_parse_colon_starting_func_args() {
// Just colon without args. // Just colon without args.

View File

@ -34,6 +34,8 @@ pub enum Token<'s> {
LeftBrace, LeftBrace,
/// A right brace in a function header: `}`. /// A right brace in a function header: `}`.
RightBrace, RightBrace,
/// A double forward chevron in a function header: `>>`.
FunctionLink,
/// A colon in a function header: `:`. /// A colon in a function header: `:`.
Colon, Colon,
@ -108,6 +110,7 @@ impl<'s> Token<'s> {
RightParen => "closing paren", RightParen => "closing paren",
LeftBrace => "opening brace", LeftBrace => "opening brace",
RightBrace => "closing brace", RightBrace => "closing brace",
FunctionLink => "function chain operator",
Colon => "colon", Colon => "colon",
Comma => "comma", Comma => "comma",
Equals => "equals sign", Equals => "equals sign",
@ -218,6 +221,8 @@ impl<'s> Iterator for Tokens<'s> {
':' if self.mode == Header => Colon, ':' if self.mode == Header => Colon,
',' if self.mode == Header => Comma, ',' if self.mode == Header => Comma,
'=' if self.mode == Header => Equals, '=' if self.mode == Header => Equals,
'>' if self.mode == Header && self.peek() == Some('>') =>
self.read_function_chain(),
// Expression operators. // Expression operators.
'+' if self.mode == Header => Plus, '+' if self.mode == Header => Plus,
@ -308,6 +313,12 @@ impl<'s> Tokens<'s> {
}, true, 0, -2).0) }, true, 0, -2).0)
} }
fn read_function_chain(&mut self) -> Token<'s> {
let second = self.eat();
debug_assert!(second == Some('>'));
FunctionLink
}
fn read_whitespace(&mut self, start: Pos) -> Token<'s> { fn read_whitespace(&mut self, start: Pos) -> Token<'s> {
self.read_string_until(|n| !n.is_whitespace(), false, 0, 0); self.read_string_until(|n| !n.is_whitespace(), false, 0, 0);
let end = self.pos(); let end = self.pos();
@ -490,6 +501,7 @@ mod tests {
LeftBracket as L, RightBracket as R, LeftBracket as L, RightBracket as R,
LeftParen as LP, RightParen as RP, LeftParen as LP, RightParen as RP,
LeftBrace as LB, RightBrace as RB, LeftBrace as LB, RightBrace as RB,
FunctionLink,
Ident as Id, Ident as Id,
Bool, Bool,
Number as Num, Number as Num,
@ -576,8 +588,10 @@ mod tests {
t!(Header, "120%" => Num(1.2)); t!(Header, "120%" => Num(1.2));
t!(Header, "12e4%" => Num(1200.0)); t!(Header, "12e4%" => Num(1200.0));
t!(Header, "__main__" => Id("__main__")); t!(Header, "__main__" => Id("__main__"));
t!(Header, ">main" => Invalid(">main"));
t!(Header, ".func.box" => Id(".func.box")); t!(Header, ".func.box" => Id(".func.box"));
t!(Header, "arg, _b, _1" => Id("arg"), Comma, S(0), Id("_b"), Comma, S(0), Id("_1")); t!(Header, "arg, _b, _1" => Id("arg"), Comma, S(0), Id("_b"), Comma, S(0), Id("_1"));
t!(Header, "f: arg >> g" => Id("f"), Colon, S(0), Id("arg"), S(0), FunctionLink, S(0), Id("g"));
t!(Header, "12_pt, 12pt" => Invalid("12_pt"), Comma, S(0), Len(Length::pt(12.0))); t!(Header, "12_pt, 12pt" => Invalid("12_pt"), Comma, S(0), Len(Length::pt(12.0)));
t!(Header, "1e5in" => Len(Length::inches(100000.0))); t!(Header, "1e5in" => Len(Length::inches(100000.0)));
t!(Header, "2.3cm" => Len(Length::cm(2.3))); t!(Header, "2.3cm" => Len(Length::cm(2.3)));