summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/syntax/parsing.rs106
-rw-r--r--src/syntax/tokens.rs13
2 files changed, 89 insertions, 30 deletions
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index 65e860cf..7d3da63e 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -67,7 +67,7 @@ impl Parser<'_> {
}
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),
@@ -111,18 +111,23 @@ impl Parser<'_> {
// Function calls.
impl Parser<'_> {
- fn parse_bracket_call(&mut self) -> Spanned<CallExpr> {
- self.start_group(Delimiter::Bracket);
- self.tokens.push_mode(TokenMode::Header);
+ fn parse_bracket_call(&mut self, chained: bool) -> Spanned<CallExpr> {
+ let before_bracket = self.pos();
+ if !chained {
+ self.start_group(Group::Bracket);
+ self.tokens.push_mode(TokenMode::Header);
+ }
- let after_bracket = self.pos();
+ let before_name = self.pos();
+ self.start_group(Group::Subheader);
self.skip_white();
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()))
});
self.skip_white();
+
let mut args = match self.eatv() {
Some(Token::Colon) => self.parse_table_contents().0,
Some(_) => {
@@ -133,11 +138,24 @@ impl Parser<'_> {
None => TableExpr::new(),
};
- self.tokens.pop_mode();
- let mut span = self.end_group();
+ 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();
+ (false, self.end_group().end)
+ };
+
+ let start = if chained { before_name } else { before_bracket };
+ let mut span = Span::new(start, end);
- if self.check(Token::LeftBracket) {
- self.start_group(Delimiter::Bracket);
+ if self.check(Token::LeftBracket) && !has_chained_child {
+ self.start_group(Group::Bracket);
self.tokens.push_mode(TokenMode::Body);
let body = self.parse_body_contents();
@@ -154,7 +172,7 @@ impl Parser<'_> {
}
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_span = self.end_group();
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
// coercable (length 1 and no trailing comma).
Token::LeftParen => {
- self.start_group(Delimiter::Paren);
+ self.start_group(Group::Paren);
let (table, coercable) = self.parse_table_contents();
let span = self.end_group();
@@ -346,7 +364,7 @@ impl Parser<'_> {
// This is a content expression.
Token::LeftBrace => {
- self.start_group(Delimiter::Brace);
+ self.start_group(Group::Brace);
self.tokens.push_mode(TokenMode::Body);
let tree = self.parse_body_contents();
@@ -358,7 +376,7 @@ impl Parser<'_> {
// This is a bracketed function call.
Token::LeftBracket => {
- let call = self.parse_bracket_call();
+ let call = self.parse_bracket_call(false);
let tree = vec![call.map(|c| SyntaxNode::Call(c))];
Spanned::new(Expr::Tree(tree), span)
}
@@ -413,17 +431,25 @@ impl Parser<'_> {
// Parsing primitives.
impl<'s> Parser<'s> {
- fn start_group(&mut self, delimiter: Delimiter) {
+ fn start_group(&mut self, group: Group) {
let start = self.pos();
- self.assert(delimiter.start());
- self.delimiters.push((start, delimiter.end()));
+ if let Some(start_token) = group.start() {
+ self.assert(start_token);
+ }
+ self.delimiters.push((start, group.end()));
}
fn end_group(&mut self) -> Span {
- assert_eq!(self.peek(), None, "unfinished group");
+ let peeked = self.peek();
+
let (start, end_token) = self.delimiters.pop()
.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() {
Some(token) if token.v == end_token => {
self.peeked = None;
@@ -431,10 +457,12 @@ impl<'s> Parser<'s> {
}
_ => {
let end = self.pos();
- error!(
- @self.feedback, Span::at(end),
- "expected {}", end_token.name(),
- );
+ if end_token != Token::Chain {
+ error!(
+ @self.feedback, Span::at(end),
+ "expected {}", end_token.name(),
+ );
+ }
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 {
self.peekv() == Some(token)
}
@@ -495,7 +524,7 @@ impl<'s> Parser<'s> {
let token = (*self.peeked.get_or_insert_with(|| tokens.next()))?;
// 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) {
return None;
}
@@ -513,25 +542,28 @@ impl<'s> Parser<'s> {
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-enum Delimiter {
+enum Group {
Paren,
Bracket,
Brace,
+ Subheader,
}
-impl Delimiter {
+impl Group {
fn is_delimiter(token: Token<'_>) -> bool {
matches!(
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 {
- Self::Paren => Token::LeftParen,
- Self::Bracket => Token::LeftBracket,
- Self::Brace => Token::LeftBrace,
+ Self::Paren => Some(Token::LeftParen),
+ Self::Bracket => Some(Token::LeftBracket),
+ Self::Brace => Some(Token::LeftBrace),
+ Self::Subheader => None,
}
}
@@ -540,6 +572,7 @@ impl Delimiter {
Self::Paren => Token::RightParen,
Self::Bracket => Token::RightBracket,
Self::Brace => Token::RightBrace,
+ Self::Subheader => Token::Chain,
}
}
}
@@ -838,6 +871,19 @@ mod tests {
}
#[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]
fn test_parse_colon_starting_func_args() {
// Just colon without args.
e!("[val:]" => );
diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs
index 50eea455..24d8ecfb 100644
--- a/src/syntax/tokens.rs
+++ b/src/syntax/tokens.rs
@@ -34,6 +34,8 @@ pub enum Token<'s> {
LeftBrace,
/// A right brace in a function header: `}`.
RightBrace,
+ /// A double forward chevron in a function header: `>>`.
+ Chain,
/// A colon in a function header: `:`.
Colon,
@@ -108,6 +110,7 @@ impl<'s> Token<'s> {
RightParen => "closing paren",
LeftBrace => "opening brace",
RightBrace => "closing brace",
+ Chain => "function chain operator",
Colon => "colon",
Comma => "comma",
Equals => "equals sign",
@@ -218,6 +221,8 @@ impl<'s> Iterator for Tokens<'s> {
':' if self.mode == Header => Colon,
',' if self.mode == Header => Comma,
'=' if self.mode == Header => Equals,
+ '>' if self.mode == Header && self.peek() == Some('>') =>
+ self.read_chain(),
// Expression operators.
'+' if self.mode == Header => Plus,
@@ -308,6 +313,11 @@ impl<'s> Tokens<'s> {
}, 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> {
self.read_string_until(|n| !n.is_whitespace(), false, 0, 0);
let end = self.pos();
@@ -490,6 +500,7 @@ mod tests {
LeftBracket as L, RightBracket as R,
LeftParen as LP, RightParen as RP,
LeftBrace as LB, RightBrace as RB,
+ Chain,
Ident as Id,
Bool,
Number as Num,
@@ -576,8 +587,10 @@ mod tests {
t!(Header, "120%" => Num(1.2));
t!(Header, "12e4%" => Num(1200.0));
t!(Header, "__main__" => Id("__main__"));
+ t!(Header, ">main" => Invalid(">main"));
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, "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, "1e5in" => Len(Length::inches(100000.0)));
t!(Header, "2.3cm" => Len(Length::cm(2.3)));