summaryrefslogtreecommitdiff
path: root/src/parse
diff options
context:
space:
mode:
authorMartin Haug <mhaug@live.de>2021-11-10 20:41:10 +0100
committerMartin Haug <mhaug@live.de>2021-11-10 20:41:10 +0100
commit3162c6a83a910f34d6ed7e966c11b7e7b5bd4088 (patch)
treece14dfbc48186a010183c637614a6f2d54f12264 /src/parse
parent91f2f97572c64d7eb25c88ad0ebb18192cf8eddf (diff)
Comments and neighbors
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/mod.rs15
-rw-r--r--src/parse/parser.rs9
-rw-r--r--src/parse/tokens.rs26
3 files changed, 39 insertions, 11 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 1f1ac266..f2fae5f2 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -26,14 +26,14 @@ pub fn parse(src: &str) -> Rc<GreenNode> {
}
/// Parse an atomic primary. Returns `Some` if all of the input was consumed.
-pub fn parse_atomic(src: &str, _: bool) -> Option<Vec<Green>> {
+pub fn parse_atomic(src: &str, _: bool) -> Option<(Vec<Green>, bool)> {
let mut p = Parser::new(src, TokenMode::Code);
primary(&mut p, true).ok()?;
p.eject_partial()
}
/// Parse some markup. Returns `Some` if all of the input was consumed.
-pub fn parse_markup(src: &str, _: bool) -> Option<Vec<Green>> {
+pub fn parse_markup(src: &str, _: bool) -> Option<(Vec<Green>, bool)> {
let mut p = Parser::new(src, TokenMode::Markup);
markup(&mut p);
p.eject()
@@ -41,7 +41,10 @@ pub fn parse_markup(src: &str, _: bool) -> Option<Vec<Green>> {
/// Parse some markup without the topmost node. Returns `Some` if all of the
/// input was consumed.
-pub fn parse_markup_elements(src: &str, mut at_start: bool) -> Option<Vec<Green>> {
+pub fn parse_markup_elements(
+ src: &str,
+ mut at_start: bool,
+) -> Option<(Vec<Green>, bool)> {
let mut p = Parser::new(src, TokenMode::Markup);
while !p.eof() {
markup_node(&mut p, &mut at_start);
@@ -50,7 +53,7 @@ pub fn parse_markup_elements(src: &str, mut at_start: bool) -> Option<Vec<Green>
}
/// Parse a template literal. Returns `Some` if all of the input was consumed.
-pub fn parse_template(source: &str, _: bool) -> Option<Vec<Green>> {
+pub fn parse_template(source: &str, _: bool) -> Option<(Vec<Green>, bool)> {
let mut p = Parser::new(source, TokenMode::Code);
if !matches!(p.peek(), Some(NodeKind::LeftBracket)) {
return None;
@@ -61,7 +64,7 @@ pub fn parse_template(source: &str, _: bool) -> Option<Vec<Green>> {
}
/// Parse a code block. Returns `Some` if all of the input was consumed.
-pub fn parse_block(source: &str, _: bool) -> Option<Vec<Green>> {
+pub fn parse_block(source: &str, _: bool) -> Option<(Vec<Green>, bool)> {
let mut p = Parser::new(source, TokenMode::Code);
if !matches!(p.peek(), Some(NodeKind::LeftBrace)) {
return None;
@@ -72,7 +75,7 @@ pub fn parse_block(source: &str, _: bool) -> Option<Vec<Green>> {
}
/// Parse a comment. Returns `Some` if all of the input was consumed.
-pub fn parse_comment(source: &str, _: bool) -> Option<Vec<Green>> {
+pub fn parse_comment(source: &str, _: bool) -> Option<(Vec<Green>, bool)> {
let mut p = Parser::new(source, TokenMode::Code);
comment(&mut p).ok()?;
p.eject()
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index 31c918a8..a37cb9c6 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -48,9 +48,9 @@ impl<'s> Parser<'s> {
}
/// End the parsing process and return multiple children.
- pub fn eject(self) -> Option<Vec<Green>> {
+ pub fn eject(self) -> Option<(Vec<Green>, bool)>{
if self.eof() && self.group_success() {
- Some(self.children)
+ Some((self.children, self.tokens.was_unterminated()))
} else {
None
}
@@ -97,8 +97,9 @@ impl<'s> Parser<'s> {
/// End the parsing process and return multiple children, even if there
/// remains stuff in the string.
- pub fn eject_partial(self) -> Option<Vec<Green>> {
- self.group_success().then(|| self.children)
+ pub fn eject_partial(self) -> Option<(Vec<Green>, bool)> {
+ self.group_success()
+ .then(|| (self.children, self.tokens.was_unterminated()))
}
/// Whether the end of the source string or group is reached.
diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs
index 27ec046d..7be31fe1 100644
--- a/src/parse/tokens.rs
+++ b/src/parse/tokens.rs
@@ -13,6 +13,7 @@ use crate::util::EcoString;
pub struct Tokens<'s> {
s: Scanner<'s>,
mode: TokenMode,
+ has_unterminated: bool,
}
/// What kind of tokens to emit.
@@ -28,7 +29,11 @@ impl<'s> Tokens<'s> {
/// Create a new token iterator with the given mode.
#[inline]
pub fn new(src: &'s str, mode: TokenMode) -> Self {
- Self { s: Scanner::new(src), mode }
+ Self {
+ s: Scanner::new(src),
+ mode,
+ has_unterminated: false,
+ }
}
/// Get the current token mode.
@@ -63,6 +68,12 @@ impl<'s> Tokens<'s> {
pub fn scanner(&self) -> Scanner<'s> {
self.s
}
+
+ /// Whether the last token was unterminated.
+ #[inline]
+ pub fn was_unterminated(&self) -> bool {
+ self.has_unterminated
+ }
}
impl<'s> Iterator for Tokens<'s> {
@@ -248,6 +259,7 @@ impl<'s> Tokens<'s> {
)
}
} else {
+ self.has_unterminated = true;
NodeKind::Error(
ErrorPos::End,
"expected closing brace".into(),
@@ -346,6 +358,7 @@ impl<'s> Tokens<'s> {
let remaining = backticks - found;
let noun = if remaining == 1 { "backtick" } else { "backticks" };
+ self.has_unterminated = true;
NodeKind::Error(
ErrorPos::End,
if found == 0 {
@@ -393,6 +406,7 @@ impl<'s> Tokens<'s> {
display,
}))
} else {
+ self.has_unterminated = true;
NodeKind::Error(
ErrorPos::End,
if !display || (!escaped && dollar) {
@@ -481,18 +495,23 @@ impl<'s> Tokens<'s> {
if self.s.eat_if('"') {
NodeKind::Str(string)
} else {
+ self.has_unterminated = true;
NodeKind::Error(ErrorPos::End, "expected quote".into())
}
}
fn line_comment(&mut self) -> NodeKind {
self.s.eat_until(is_newline);
+ if self.s.peek().is_none() {
+ self.has_unterminated = true;
+ }
NodeKind::LineComment
}
fn block_comment(&mut self) -> NodeKind {
let mut state = '_';
let mut depth = 1;
+ let mut terminated = false;
// Find the first `*/` that does not correspond to a nested `/*`.
while let Some(c) = self.s.eat() {
@@ -500,6 +519,7 @@ impl<'s> Tokens<'s> {
('*', '/') => {
depth -= 1;
if depth == 0 {
+ terminated = true;
break;
}
'_'
@@ -512,6 +532,10 @@ impl<'s> Tokens<'s> {
}
}
+ if !terminated {
+ self.has_unterminated = true;
+ }
+
NodeKind::BlockComment
}