parent
b9d03be975
commit
017027bf39
@ -80,12 +80,6 @@ impl Lexer<'_> {
|
|||||||
self.error = Some((message.into(), ErrorPos::Full));
|
self.error = Some((message.into(), ErrorPos::Full));
|
||||||
SyntaxKind::Error
|
SyntaxKind::Error
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a positioned syntax error.
|
|
||||||
fn error_at_end(&mut self, message: impl Into<EcoString>) -> SyntaxKind {
|
|
||||||
self.error = Some((message.into(), ErrorPos::End));
|
|
||||||
SyntaxKind::Error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shared.
|
/// Shared.
|
||||||
@ -209,7 +203,7 @@ impl Lexer<'_> {
|
|||||||
if self.s.eat_if("u{") {
|
if self.s.eat_if("u{") {
|
||||||
let hex = self.s.eat_while(char::is_ascii_alphanumeric);
|
let hex = self.s.eat_while(char::is_ascii_alphanumeric);
|
||||||
if !self.s.eat_if('}') {
|
if !self.s.eat_if('}') {
|
||||||
return self.error_at_end("expected closing brace");
|
return self.error("unclosed unicode escape sequence");
|
||||||
}
|
}
|
||||||
|
|
||||||
if u32::from_str_radix(hex, 16)
|
if u32::from_str_radix(hex, 16)
|
||||||
@ -251,20 +245,15 @@ impl Lexer<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if found != backticks {
|
if found != backticks {
|
||||||
let remaining = backticks - found;
|
return self.error("unclosed raw text");
|
||||||
let noun = if remaining == 1 { "backtick" } else { "backticks" };
|
|
||||||
return self.error_at_end(if found == 0 {
|
|
||||||
eco_format!("expected {} {}", remaining, noun)
|
|
||||||
} else {
|
|
||||||
eco_format!("expected {} more {}", remaining, noun)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SyntaxKind::Raw
|
SyntaxKind::Raw
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link(&mut self) -> SyntaxKind {
|
fn link(&mut self) -> SyntaxKind {
|
||||||
let mut bracket_stack = Vec::new();
|
let mut brackets = Vec::new();
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
self.s.eat_while(|c: char| {
|
self.s.eat_while(|c: char| {
|
||||||
match c {
|
match c {
|
||||||
@ -275,20 +264,24 @@ impl Lexer<'_> {
|
|||||||
| ',' | '-' | '.' | '/' | ':' | ';' | '='
|
| ',' | '-' | '.' | '/' | ':' | ';' | '='
|
||||||
| '?' | '@' | '_' | '~' | '\'' => true,
|
| '?' | '@' | '_' | '~' | '\'' => true,
|
||||||
'[' => {
|
'[' => {
|
||||||
bracket_stack.push(SyntaxKind::LeftBracket);
|
brackets.push(SyntaxKind::LeftBracket);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
'(' => {
|
'(' => {
|
||||||
bracket_stack.push(SyntaxKind::LeftParen);
|
brackets.push(SyntaxKind::LeftParen);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
']' => bracket_stack.pop() == Some(SyntaxKind::LeftBracket),
|
']' => brackets.pop() == Some(SyntaxKind::LeftBracket),
|
||||||
')' => bracket_stack.pop() == Some(SyntaxKind::LeftParen),
|
')' => brackets.pop() == Some(SyntaxKind::LeftParen),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if !bracket_stack.is_empty() {
|
|
||||||
return self.error_at_end("expected closing bracket in link");
|
if !brackets.is_empty() {
|
||||||
|
return self.error(
|
||||||
|
"automatic links cannot contain unbalanced brackets, \
|
||||||
|
use the `link` function instead",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't include the trailing characters likely to be part of text.
|
// Don't include the trailing characters likely to be part of text.
|
||||||
@ -328,7 +321,7 @@ impl Lexer<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !self.s.eat_if('>') {
|
if !self.s.eat_if('>') {
|
||||||
return self.error_at_end("expected closing angle bracket");
|
return self.error("unclosed label");
|
||||||
}
|
}
|
||||||
|
|
||||||
SyntaxKind::Label
|
SyntaxKind::Label
|
||||||
@ -620,7 +613,7 @@ impl Lexer<'_> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if !self.s.eat_if('"') {
|
if !self.s.eat_if('"') {
|
||||||
return self.error_at_end("expected quote");
|
return self.error("unclosed string");
|
||||||
}
|
}
|
||||||
|
|
||||||
SyntaxKind::Str
|
SyntaxKind::Str
|
||||||
|
@ -71,7 +71,7 @@ pub(super) fn reparse_markup(
|
|||||||
match p.current() {
|
match p.current() {
|
||||||
SyntaxKind::LeftBracket => *nesting += 1,
|
SyntaxKind::LeftBracket => *nesting += 1,
|
||||||
SyntaxKind::RightBracket if *nesting > 0 => *nesting -= 1,
|
SyntaxKind::RightBracket if *nesting > 0 => *nesting -= 1,
|
||||||
_ if stop(p.current) => break,
|
_ if stop(p.current()) => break,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ fn strong(p: &mut Parser) {
|
|||||||
|| p.at(SyntaxKind::Parbreak)
|
|| p.at(SyntaxKind::Parbreak)
|
||||||
|| p.at(SyntaxKind::RightBracket)
|
|| p.at(SyntaxKind::RightBracket)
|
||||||
});
|
});
|
||||||
p.expect(SyntaxKind::Star);
|
p.expect_closing_delimiter(m, SyntaxKind::Star);
|
||||||
p.wrap(m, SyntaxKind::Strong);
|
p.wrap(m, SyntaxKind::Strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ fn emph(p: &mut Parser) {
|
|||||||
|| p.at(SyntaxKind::Parbreak)
|
|| p.at(SyntaxKind::Parbreak)
|
||||||
|| p.at(SyntaxKind::RightBracket)
|
|| p.at(SyntaxKind::RightBracket)
|
||||||
});
|
});
|
||||||
p.expect(SyntaxKind::Underscore);
|
p.expect_closing_delimiter(m, SyntaxKind::Underscore);
|
||||||
p.wrap(m, SyntaxKind::Emph);
|
p.wrap(m, SyntaxKind::Emph);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,15 +220,15 @@ fn equation(p: &mut Parser) {
|
|||||||
let m = p.marker();
|
let m = p.marker();
|
||||||
p.enter(LexMode::Math);
|
p.enter(LexMode::Math);
|
||||||
p.assert(SyntaxKind::Dollar);
|
p.assert(SyntaxKind::Dollar);
|
||||||
math(p, |kind| kind == SyntaxKind::Dollar);
|
math(p, |p| p.at(SyntaxKind::Dollar));
|
||||||
p.expect(SyntaxKind::Dollar);
|
p.expect_closing_delimiter(m, SyntaxKind::Dollar);
|
||||||
p.exit();
|
p.exit();
|
||||||
p.wrap(m, SyntaxKind::Equation);
|
p.wrap(m, SyntaxKind::Equation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn math(p: &mut Parser, mut stop: impl FnMut(SyntaxKind) -> bool) {
|
fn math(p: &mut Parser, mut stop: impl FnMut(&Parser) -> bool) {
|
||||||
let m = p.marker();
|
let m = p.marker();
|
||||||
while !p.eof() && !stop(p.current()) {
|
while !p.eof() && !stop(p) {
|
||||||
let prev = p.prev_end();
|
let prev = p.prev_end();
|
||||||
math_expr(p);
|
math_expr(p);
|
||||||
if !p.progress(prev) {
|
if !p.progress(prev) {
|
||||||
@ -514,22 +514,18 @@ fn maybe_wrap_in_math(p: &mut Parser, arg: Marker, named: Option<Marker>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code(p: &mut Parser, stop: impl FnMut(SyntaxKind) -> bool) {
|
fn code(p: &mut Parser, stop: impl FnMut(&Parser) -> bool) {
|
||||||
let m = p.marker();
|
let m = p.marker();
|
||||||
code_exprs(p, stop);
|
code_exprs(p, stop);
|
||||||
p.wrap(m, SyntaxKind::Code);
|
p.wrap(m, SyntaxKind::Code);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code_exprs(p: &mut Parser, mut stop: impl FnMut(SyntaxKind) -> bool) {
|
fn code_exprs(p: &mut Parser, mut stop: impl FnMut(&Parser) -> bool) {
|
||||||
while !p.eof() && !stop(p.current()) {
|
while !p.eof() && !stop(p) {
|
||||||
p.stop_at_newline(true);
|
p.stop_at_newline(true);
|
||||||
let prev = p.prev_end();
|
let prev = p.prev_end();
|
||||||
code_expr(p);
|
code_expr(p);
|
||||||
if p.progress(prev)
|
if p.progress(prev) && !p.eof() && !stop(p) && !p.eat_if(SyntaxKind::Semicolon) {
|
||||||
&& !p.eof()
|
|
||||||
&& !stop(p.current())
|
|
||||||
&& !p.eat_if(SyntaxKind::Semicolon)
|
|
||||||
{
|
|
||||||
p.expected("semicolon or line break");
|
p.expected("semicolon or line break");
|
||||||
}
|
}
|
||||||
p.unstop();
|
p.unstop();
|
||||||
@ -725,8 +721,12 @@ fn code_block(p: &mut Parser) {
|
|||||||
p.enter(LexMode::Code);
|
p.enter(LexMode::Code);
|
||||||
p.stop_at_newline(false);
|
p.stop_at_newline(false);
|
||||||
p.assert(SyntaxKind::LeftBrace);
|
p.assert(SyntaxKind::LeftBrace);
|
||||||
code(p, |kind| kind == SyntaxKind::RightBrace);
|
code(p, |p| {
|
||||||
p.expect(SyntaxKind::RightBrace);
|
p.at(SyntaxKind::RightBrace)
|
||||||
|
|| p.at(SyntaxKind::RightBracket)
|
||||||
|
|| p.at(SyntaxKind::RightParen)
|
||||||
|
});
|
||||||
|
p.expect_closing_delimiter(m, SyntaxKind::RightBrace);
|
||||||
p.exit();
|
p.exit();
|
||||||
p.unstop();
|
p.unstop();
|
||||||
p.wrap(m, SyntaxKind::CodeBlock);
|
p.wrap(m, SyntaxKind::CodeBlock);
|
||||||
@ -737,7 +737,7 @@ fn content_block(p: &mut Parser) {
|
|||||||
p.enter(LexMode::Markup);
|
p.enter(LexMode::Markup);
|
||||||
p.assert(SyntaxKind::LeftBracket);
|
p.assert(SyntaxKind::LeftBracket);
|
||||||
markup(p, true, 0, |p| p.at(SyntaxKind::RightBracket));
|
markup(p, true, 0, |p| p.at(SyntaxKind::RightBracket));
|
||||||
p.expect(SyntaxKind::RightBracket);
|
p.expect_closing_delimiter(m, SyntaxKind::RightBracket);
|
||||||
p.exit();
|
p.exit();
|
||||||
p.wrap(m, SyntaxKind::ContentBlock);
|
p.wrap(m, SyntaxKind::ContentBlock);
|
||||||
}
|
}
|
||||||
@ -800,6 +800,8 @@ fn invalidate_destructuring(p: &mut Parser, m: Marker) {
|
|||||||
|
|
||||||
fn collection(p: &mut Parser, keyed: bool) -> SyntaxKind {
|
fn collection(p: &mut Parser, keyed: bool) -> SyntaxKind {
|
||||||
p.stop_at_newline(false);
|
p.stop_at_newline(false);
|
||||||
|
|
||||||
|
let m = p.marker();
|
||||||
p.assert(SyntaxKind::LeftParen);
|
p.assert(SyntaxKind::LeftParen);
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@ -845,7 +847,7 @@ fn collection(p: &mut Parser, keyed: bool) -> SyntaxKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.expect(SyntaxKind::RightParen);
|
p.expect_closing_delimiter(m, SyntaxKind::RightParen);
|
||||||
p.unstop();
|
p.unstop();
|
||||||
|
|
||||||
if parenthesized && count == 1 {
|
if parenthesized && count == 1 {
|
||||||
@ -1586,6 +1588,12 @@ impl<'s> Parser<'s> {
|
|||||||
at
|
at
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expect_closing_delimiter(&mut self, open: Marker, kind: SyntaxKind) {
|
||||||
|
if !self.eat_if(kind) {
|
||||||
|
self.nodes[open.0].convert_to_error("unclosed delimiter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expected(&mut self, thing: &str) {
|
fn expected(&mut self, thing: &str) {
|
||||||
self.unskip();
|
self.unskip();
|
||||||
if self
|
if self
|
||||||
|
@ -306,15 +306,15 @@ mod tests {
|
|||||||
test("Hello #{ x + 1 }!", 9..10, "abc", true);
|
test("Hello #{ x + 1 }!", 9..10, "abc", true);
|
||||||
test("A#{}!", 3..3, "\"", false);
|
test("A#{}!", 3..3, "\"", false);
|
||||||
test("#{ [= x] }!", 5..5, "=", true);
|
test("#{ [= x] }!", 5..5, "=", true);
|
||||||
test("#[[]]", 3..3, "\\", false);
|
test("#[[]]", 3..3, "\\", true);
|
||||||
test("#[[ab]]", 4..5, "\\", false);
|
test("#[[ab]]", 4..5, "\\", true);
|
||||||
test("#{}}", 2..2, "{", false);
|
test("#{}}", 2..2, "{", false);
|
||||||
test("A: #[BC]", 6..6, "{", true);
|
test("A: #[BC]", 6..6, "{", true);
|
||||||
test("A: #[BC]", 6..6, "#{", false);
|
test("A: #[BC]", 6..6, "#{", true);
|
||||||
test("A: #[BC]", 6..6, "#{}", true);
|
test("A: #[BC]", 6..6, "#{}", true);
|
||||||
test("#{\"ab\"}A", 5..5, "c", true);
|
test("#{\"ab\"}A", 5..5, "c", true);
|
||||||
test("#{\"ab\"}A", 5..6, "c", false);
|
test("#{\"ab\"}A", 5..6, "c", false);
|
||||||
test("a#[]b", 3..3, "#{", false);
|
test("a#[]b", 3..3, "#{", true);
|
||||||
test("a#{call(); abc}b", 8..8, "[]", true);
|
test("a#{call(); abc}b", 8..8, "[]", true);
|
||||||
test("a #while x {\n g(x) \n} b", 12..12, "//", true);
|
test("a #while x {\n g(x) \n} b", 12..12, "//", true);
|
||||||
test("a#[]b", 3..3, "[hey]", true);
|
test("a#[]b", 3..3, "[hey]", true);
|
||||||
|
@ -256,10 +256,10 @@
|
|||||||
#(1, 2, 3).at(-4)
|
#(1, 2, 3).at(-4)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 4 expected closing paren
|
// Error: 3-4 unclosed delimiter
|
||||||
#{(}
|
#{(}
|
||||||
|
|
||||||
// Error: 3-4 unexpected closing paren
|
// Error: 2-3 unclosed delimiter
|
||||||
#{)}
|
#{)}
|
||||||
|
|
||||||
// Error: 4-6 unexpected end of block comment
|
// Error: 4-6 unexpected end of block comment
|
||||||
|
@ -138,7 +138,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 3 expected closing brace
|
// Error: 2-3 unclosed delimiter
|
||||||
#{
|
#{
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -94,13 +94,14 @@
|
|||||||
#func((x):1)
|
#func((x):1)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 2:1 expected closing bracket
|
// Error: 6-7 unclosed delimiter
|
||||||
#func[`a]`
|
#func[`a]`
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 8 expected closing paren
|
// Error: 7-8 unclosed delimiter
|
||||||
#{func(}
|
#{func(}
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 2:1 expected quote
|
// Error: 6-7 unclosed delimiter
|
||||||
|
// Error: 1:7-2:1 unclosed string
|
||||||
#func("]
|
#func("]
|
||||||
|
@ -227,7 +227,7 @@ Three
|
|||||||
|
|
||||||
// Terminated by semicolon even though we are in a paren group.
|
// Terminated by semicolon even though we are in a paren group.
|
||||||
// Error: 18 expected expression
|
// Error: 18 expected expression
|
||||||
// Error: 18 expected closing paren
|
// Error: 11-12 unclosed delimiter
|
||||||
#let v5 = (1, 2 + ; Five
|
#let v5 = (1, 2 + ; Five
|
||||||
|
|
||||||
// Error: 9-13 expected identifier, found boolean
|
// Error: 9-13 expected identifier, found boolean
|
||||||
|
@ -32,5 +32,5 @@
|
|||||||
#test((1), 1)
|
#test((1), 1)
|
||||||
#test((1+2)*-3, -9)
|
#test((1+2)*-3, -9)
|
||||||
|
|
||||||
// Error: 14 expected closing paren
|
// Error: 8-9 unclosed delimiter
|
||||||
#test({(1 + 1}, 2)
|
#test({(1 + 1}, 2)
|
||||||
|
@ -204,5 +204,5 @@
|
|||||||
#test("a123c".split(regex("\d+")), ("a", "c"))
|
#test("a123c".split(regex("\d+")), ("a", "c"))
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 2:1 expected quote
|
// Error: 2-2:1 unclosed string
|
||||||
#"hello\"
|
#"hello\"
|
||||||
|
@ -18,5 +18,5 @@ $ underline(f' : NN -> RR) \
|
|||||||
$ dot \ dots \ ast \ tilde \ star $
|
$ dot \ dots \ ast \ tilde \ star $
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 1:3 expected dollar sign
|
// Error: 1-2 unclosed delimiter
|
||||||
$a
|
$a
|
||||||
|
@ -35,7 +35,7 @@ https://example.com/)
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Verify that opening brackets without closing brackets throw an error.
|
// Verify that opening brackets without closing brackets throw an error.
|
||||||
// Error: 22-22 expected closing bracket in link
|
// Error: 1-22 automatic links cannot contain unbalanced brackets, use the `link` function instead
|
||||||
https://exam(ple.com/
|
https://exam(ple.com/
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -27,17 +27,17 @@ Normal
|
|||||||
*Medium* and *#[*Bold*]*
|
*Medium* and *#[*Bold*]*
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 13 expected underscore
|
// Error: 6-7 unclosed delimiter
|
||||||
#box[_Scoped] to body.
|
#box[_Scoped] to body.
|
||||||
|
|
||||||
---
|
---
|
||||||
// Ends at paragraph break.
|
// Ends at paragraph break.
|
||||||
// Error: 7 expected underscore
|
// Error: 1-2 unclosed delimiter
|
||||||
_Hello
|
_Hello
|
||||||
|
|
||||||
World
|
World
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 26 expected star
|
// Error: 11-12 unclosed delimiter
|
||||||
// Error: 26 expected underscore
|
// Error: 3-4 unclosed delimiter
|
||||||
#[_Cannot *be interleaved]
|
#[_Cannot *be interleaved]
|
||||||
|
@ -32,5 +32,5 @@ let f() , ; : | + - /= == 12 "string"
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Unterminated.
|
// Unterminated.
|
||||||
// Error: 6 expected closing brace
|
// Error: 1-6 unclosed unicode escape sequence
|
||||||
\u{41[*Bold*]
|
\u{41[*Bold*]
|
||||||
|
@ -55,5 +55,5 @@ The keyword ```rust let```.
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Unterminated.
|
// Unterminated.
|
||||||
// Error: 2:1 expected 1 backtick
|
// Error: 1-2:1 unclosed raw text
|
||||||
`endless
|
`endless
|
||||||
|
Loading…
x
Reference in New Issue
Block a user