summaryrefslogtreecommitdiff
path: root/src/parse
diff options
context:
space:
mode:
authorMartin Haug <mhaug@live.de>2021-11-06 16:07:21 +0100
committerMartin Haug <mhaug@live.de>2021-11-06 16:07:21 +0100
commiteba7fc34effbec3bcc6d5c40d831b1e15af77c4d (patch)
tree5e44d0862637ce7bfeb090069f3ad2dce9017ab8 /src/parse
parent7016ab0d123ba06d0bbc6ed5001fa02fbd261bfa (diff)
Incremental-safety based approach
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/mod.rs71
-rw-r--r--src/parse/parser.rs24
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) {