diff options
| author | Martin Haug <mhaug@live.de> | 2021-11-06 16:07:21 +0100 |
|---|---|---|
| committer | Martin Haug <mhaug@live.de> | 2021-11-06 16:07:21 +0100 |
| commit | eba7fc34effbec3bcc6d5c40d831b1e15af77c4d (patch) | |
| tree | 5e44d0862637ce7bfeb090069f3ad2dce9017ab8 /src/parse | |
| parent | 7016ab0d123ba06d0bbc6ed5001fa02fbd261bfa (diff) | |
Incremental-safety based approach
Diffstat (limited to 'src/parse')
| -rw-r--r-- | src/parse/mod.rs | 71 | ||||
| -rw-r--r-- | src/parse/parser.rs | 24 |
2 files changed, 71 insertions, 24 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 1ab2fb15..5d845a55 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -25,18 +25,36 @@ pub fn parse(src: &str) -> Rc<GreenNode> { } } -/// Parse a block. Returns `Some` if there was only one block. -pub fn parse_block(source: &str) -> Option<Rc<GreenNode>> { +/// Parse an atomic primary. Returns `Some` if all of the input was consumed. +pub fn parse_atomic(source: &str, _: bool) -> Option<Vec<Green>> { let mut p = Parser::new(source); - block(&mut p); - if p.eof() { - match p.finish().into_iter().next() { - Some(Green::Node(node)) => Some(node), - _ => unreachable!(), - } - } else { - None + primary(&mut p, true).ok()?; + p.eject() +} + +/// Parse some markup. Returns `Some` if all of the input was consumed. +pub fn parse_markup(source: &str, _: bool) -> Option<Vec<Green>> { + let mut p = Parser::new(source); + markup(&mut p); + p.eject() +} + +/// Parse some markup without the topmost node. Returns `Some` if all of the +/// input was consumed. +pub fn parse_markup_elements(source: &str, mut at_start: bool) -> Option<Vec<Green>> { + let mut p = Parser::new(source); + while !p.eof() { + markup_node(&mut p, &mut at_start); } + p.eject() +} + +/// Parse some code. Returns `Some` if all of the input was consumed. +pub fn parse_code(source: &str, _: bool) -> Option<Vec<Green>> { + let mut p = Parser::new(source); + p.set_mode(TokenMode::Code); + expr_list(&mut p); + p.eject() } /// Parse markup. @@ -118,7 +136,7 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) { // Line-based markup that is not currently at the start of the line. NodeKind::Eq | NodeKind::Minus | NodeKind::EnumNumbering(_) => { - p.convert(NodeKind::Text(p.peek_src().into())); + p.convert(NodeKind::TextInLine(p.peek_src().into())) } // Hashtag + keyword / identifier. @@ -196,7 +214,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult { let marker = p.marker(); // Start the unary expression. - match p.peek().and_then(UnOp::from_token) { + match (!atomic).then(|| p.peek().and_then(UnOp::from_token)).flatten() { Some(op) => { p.eat(); let prec = op.precedence(); @@ -268,7 +286,7 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult { } // Structures. - Some(NodeKind::LeftParen) => parenthesized(p), + Some(NodeKind::LeftParen) => parenthesized(p, atomic), Some(NodeKind::LeftBracket) => { template(p); Ok(()) @@ -329,7 +347,7 @@ fn literal(p: &mut Parser) -> bool { /// - Dictionary literal /// - Parenthesized expression /// - Parameter list of closure expression -fn parenthesized(p: &mut Parser) -> ParseResult { +fn parenthesized(p: &mut Parser, atomic: bool) -> ParseResult { let marker = p.marker(); p.start_group(Group::Paren); @@ -344,7 +362,7 @@ fn parenthesized(p: &mut Parser) -> ParseResult { } // Arrow means this is a closure's parameter list. - if p.at(&NodeKind::Arrow) { + if !atomic && p.at(&NodeKind::Arrow) { params(p, marker); p.eat_assert(&NodeKind::Arrow); return marker.perform(p, NodeKind::Closure, expr); @@ -507,18 +525,23 @@ fn template(p: &mut Parser) { fn block(p: &mut Parser) { p.perform(NodeKind::Block, |p| { p.start_group(Group::Brace); - while !p.eof() { - p.start_group(Group::Stmt); - if expr(p).is_ok() && !p.eof() { - p.expected_at("semicolon or line break"); - } - p.end_group(); + expr_list(p); + p.end_group(); + }); +} - // Forcefully skip over newlines since the group's contents can't. - p.eat_while(|t| matches!(t, NodeKind::Space(_))); +/// Parse a number of code expressions. +fn expr_list(p: &mut Parser) { + while !p.eof() { + p.start_group(Group::Stmt); + if expr(p).is_ok() && !p.eof() { + p.expected_at("semicolon or line break"); } p.end_group(); - }); + + // Forcefully skip over newlines since the group's contents can't. + p.eat_while(|t| matches!(t, NodeKind::Space(_))); + } } /// Parse a function call. diff --git a/src/parse/parser.rs b/src/parse/parser.rs index af8a7c5c..f391c473 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -21,6 +21,8 @@ pub struct Parser<'s> { groups: Vec<GroupEntry>, /// The children of the currently built node. children: Vec<Green>, + /// Whether the last group was terminated. + last_group_terminated: bool, } impl<'s> Parser<'s> { @@ -36,6 +38,7 @@ impl<'s> Parser<'s> { current_start: 0, groups: vec![], children: vec![], + last_group_terminated: true, } } @@ -44,6 +47,15 @@ impl<'s> Parser<'s> { self.children } + /// End the parsing process and return multiple children. + pub fn eject(self) -> Option<Vec<Green>> { + if self.eof() && self.group_success() { + Some(self.children) + } else { + None + } + } + /// Create a new marker. pub fn marker(&mut self) -> Marker { Marker(self.children.len()) @@ -190,6 +202,11 @@ impl<'s> Parser<'s> { self.tokens.scanner().column(index) } + /// Set the tokenizer's mode. + pub fn set_mode(&mut self, mode: TokenMode) { + self.tokens.set_mode(mode); + } + /// Continue parsing in a group. /// /// When the end delimiter of the group is reached, all subsequent calls to @@ -225,6 +242,7 @@ impl<'s> Parser<'s> { let group = self.groups.pop().expect("no started group"); self.tokens.set_mode(group.prev_mode); self.repeek(); + self.last_group_terminated = true; let mut rescan = self.tokens.mode() != group_mode; @@ -243,6 +261,7 @@ impl<'s> Parser<'s> { rescan = false; } else if required { self.push_error(format_eco!("expected {}", end)); + self.last_group_terminated = false; } } @@ -260,6 +279,11 @@ impl<'s> Parser<'s> { } } + /// Check if the group processing was successfully terminated. + pub fn group_success(&self) -> bool { + self.last_group_terminated && self.groups.is_empty() + } + /// Low-level bump that consumes exactly one token without special trivia /// handling. fn bump(&mut self) { |
