Merge pull request #10 from typst/multi-invocation

Add Multi-Invokation 👨‍👩‍👧‍👧
This commit is contained in:
Laurenz 2020-08-27 21:21:23 +02:00 committed by GitHub
commit ad8e391c69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 30 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,18 +111,23 @@ 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> {
self.start_group(Delimiter::Bracket); let before_bracket = self.pos();
if !chained {
self.start_group(Group::Bracket);
self.tokens.push_mode(TokenMode::Header); self.tokens.push_mode(TokenMode::Header);
}
let after_bracket = self.pos(); let before_name = self.pos();
self.start_group(Group::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", before_name);
Spanned::zero(Ident(String::new())) Spanned::zero(Ident(String::new()))
}); });
self.skip_white(); self.skip_white();
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(_) => {
@ -133,11 +138,24 @@ impl Parser<'_> {
None => TableExpr::new(), None => TableExpr::new(),
}; };
self.end_group();
self.skip_white();
let (has_chained_child, end) = if self.peek().is_some() {
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)));
(true, span.end)
} else {
self.tokens.pop_mode(); self.tokens.pop_mode();
let mut span = self.end_group(); (false, self.end_group().end)
};
if self.check(Token::LeftBracket) { let start = if chained { before_name } else { before_bracket };
self.start_group(Delimiter::Bracket); let mut span = Span::new(start, end);
if self.check(Token::LeftBracket) && !has_chained_child {
self.start_group(Group::Bracket);
self.tokens.push_mode(TokenMode::Body); self.tokens.push_mode(TokenMode::Body);
let body = self.parse_body_contents(); let body = self.parse_body_contents();
@ -154,7 +172,7 @@ impl Parser<'_> {
} }
fn parse_paren_call(&mut self, name: Spanned<Ident>) -> Spanned<CallExpr> { fn parse_paren_call(&mut self, name: Spanned<Ident>) -> Spanned<CallExpr> {
self.start_group(Delimiter::Paren); self.start_group(Group::Paren);
let args = self.parse_table_contents().0; let args = self.parse_table_contents().0;
let args_span = self.end_group(); let args_span = self.end_group();
let span = Span::merge(name.span, args_span); let span = Span::merge(name.span, args_span);
@ -329,7 +347,7 @@ impl Parser<'_> {
// a table in any case and coerce the table into a value if it is // a table in any case and coerce the table into a value if it is
// coercable (length 1 and no trailing comma). // coercable (length 1 and no trailing comma).
Token::LeftParen => { Token::LeftParen => {
self.start_group(Delimiter::Paren); self.start_group(Group::Paren);
let (table, coercable) = self.parse_table_contents(); let (table, coercable) = self.parse_table_contents();
let span = self.end_group(); let span = self.end_group();
@ -346,7 +364,7 @@ impl Parser<'_> {
// This is a content expression. // This is a content expression.
Token::LeftBrace => { Token::LeftBrace => {
self.start_group(Delimiter::Brace); self.start_group(Group::Brace);
self.tokens.push_mode(TokenMode::Body); self.tokens.push_mode(TokenMode::Body);
let tree = self.parse_body_contents(); let tree = self.parse_body_contents();
@ -358,7 +376,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)
} }
@ -413,17 +431,25 @@ impl Parser<'_> {
// Parsing primitives. // Parsing primitives.
impl<'s> Parser<'s> { impl<'s> Parser<'s> {
fn start_group(&mut self, delimiter: Delimiter) { fn start_group(&mut self, group: Group) {
let start = self.pos(); let start = self.pos();
self.assert(delimiter.start()); if let Some(start_token) = group.start() {
self.delimiters.push((start, delimiter.end())); self.assert(start_token);
}
self.delimiters.push((start, group.end()));
} }
fn end_group(&mut self) -> Span { fn end_group(&mut self) -> Span {
assert_eq!(self.peek(), None, "unfinished group"); let peeked = self.peek();
let (start, end_token) = self.delimiters.pop() let (start, end_token) = self.delimiters.pop()
.expect("group was not started"); .expect("group was not started");
if end_token != Token::Chain && peeked != None {
self.delimiters.push((start, end_token));
assert_eq!(peeked, None, "unfinished group");
}
match self.peeked.unwrap() { match self.peeked.unwrap() {
Some(token) if token.v == end_token => { Some(token) if token.v == end_token => {
self.peeked = None; self.peeked = None;
@ -431,10 +457,12 @@ impl<'s> Parser<'s> {
} }
_ => { _ => {
let end = self.pos(); let end = self.pos();
if end_token != Token::Chain {
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 +499,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)
} }
@ -495,7 +524,7 @@ impl<'s> Parser<'s> {
let token = (*self.peeked.get_or_insert_with(|| tokens.next()))?; let token = (*self.peeked.get_or_insert_with(|| tokens.next()))?;
// Check for unclosed groups. // Check for unclosed groups.
if Delimiter::is_delimiter(token.v) { if Group::is_delimiter(token.v) {
if self.delimiters.iter().rev().any(|&(_, end)| token.v == end) { if self.delimiters.iter().rev().any(|&(_, end)| token.v == end) {
return None; return None;
} }
@ -513,25 +542,28 @@ impl<'s> Parser<'s> {
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum Delimiter { enum Group {
Paren, Paren,
Bracket, Bracket,
Brace, Brace,
Subheader,
} }
impl Delimiter { impl Group {
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::Chain
) )
} }
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 +572,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::Chain,
} }
} }
} }
@ -837,6 +870,19 @@ 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")]))])]);
t!("[box >>][Hi]" => P![F!("box"; 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: `>>`.
Chain,
/// 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",
Chain => "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_chain(),
// Expression operators. // Expression operators.
'+' if self.mode == Header => Plus, '+' if self.mode == Header => Plus,
@ -308,6 +313,11 @@ impl<'s> Tokens<'s> {
}, true, 0, -2).0) }, true, 0, -2).0)
} }
fn read_chain(&mut self) -> Token<'s> {
assert!(self.eat() == Some('>'));
Chain
}
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 +500,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,
Chain,
Ident as Id, Ident as Id,
Bool, Bool,
Number as Num, Number as Num,
@ -576,8 +587,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), Chain, 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)));