mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
parent
46a6f92bf3
commit
adcc6e5506
@ -304,6 +304,32 @@ impl SyntaxKind {
|
||||
)
|
||||
}
|
||||
|
||||
/// Is this node is a keyword.
|
||||
pub fn is_keyword(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Not
|
||||
| Self::And
|
||||
| Self::Or
|
||||
| Self::None
|
||||
| Self::Auto
|
||||
| Self::Let
|
||||
| Self::Set
|
||||
| Self::Show
|
||||
| Self::If
|
||||
| Self::Else
|
||||
| Self::For
|
||||
| Self::In
|
||||
| Self::While
|
||||
| Self::Break
|
||||
| Self::Continue
|
||||
| Self::Return
|
||||
| Self::Import
|
||||
| Self::Include
|
||||
| Self::As
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether this kind of node is automatically skipped by the parser in
|
||||
/// code and math mode.
|
||||
pub fn is_trivia(self) -> bool {
|
||||
|
@ -1583,11 +1583,16 @@ impl<'s> Parser<'s> {
|
||||
self.current = SyntaxKind::Eof;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Parser<'s> {
|
||||
/// Consume the given syntax `kind` or produce an error.
|
||||
fn expect(&mut self, kind: SyntaxKind) -> bool {
|
||||
let at = self.at(kind);
|
||||
if at {
|
||||
self.eat();
|
||||
} else if kind == SyntaxKind::Ident && self.current.is_keyword() {
|
||||
self.expected_found(kind.name(), self.current.name());
|
||||
} else {
|
||||
self.balanced &= !kind.is_grouping();
|
||||
self.expected(kind.name());
|
||||
@ -1595,26 +1600,75 @@ impl<'s> Parser<'s> {
|
||||
at
|
||||
}
|
||||
|
||||
/// Produce an error that the given `thing` was expected.
|
||||
fn expected(&mut self, thing: &str) {
|
||||
self.unskip();
|
||||
if !self.after_error() {
|
||||
let message = eco_format!("expected {thing}");
|
||||
self.nodes.push(SyntaxNode::error(message, ""));
|
||||
}
|
||||
self.skip();
|
||||
}
|
||||
|
||||
/// Produce an error that the given `thing` was expected but another
|
||||
/// thing was `found` and consumethe next token.
|
||||
fn expected_found(&mut self, thing: &str, found: &str) {
|
||||
self.unskip();
|
||||
if !self.after_error() {
|
||||
self.skip();
|
||||
self.convert_to_error(eco_format!("expected {thing}, found {found}"));
|
||||
}
|
||||
self.skip();
|
||||
}
|
||||
|
||||
/// Produce an error that the given `thing` was expected at the position
|
||||
/// of the marker `m`.
|
||||
fn expected_at(&mut self, m: Marker, thing: &str) {
|
||||
let message = eco_format!("expected {}", thing);
|
||||
let error = SyntaxNode::error(message, "");
|
||||
self.nodes.insert(m.0, error);
|
||||
}
|
||||
|
||||
/// Produce an error for the unclosed delimiter `kind` at the position
|
||||
/// `open`.
|
||||
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) {
|
||||
/// Consume the next token and produce an error stating that it was
|
||||
/// unexpected.
|
||||
fn unexpected(&mut self) {
|
||||
self.unskip();
|
||||
if self
|
||||
while self
|
||||
.nodes
|
||||
.last()
|
||||
.map_or(true, |child| child.kind() != SyntaxKind::Error)
|
||||
.map_or(false, |child| child.kind().is_error() && child.is_empty())
|
||||
{
|
||||
let message = eco_format!("expected {}", thing);
|
||||
self.nodes.push(SyntaxNode::error(message, ""));
|
||||
self.nodes.pop();
|
||||
}
|
||||
self.skip();
|
||||
self.convert_to_error(eco_format!("unexpected {}", self.current.name()));
|
||||
}
|
||||
|
||||
// Adds a hint to the last node, if the last node is an error.
|
||||
/// Whether the last node is an error.
|
||||
fn after_error(&self) -> bool {
|
||||
self.nodes.last().map_or(false, |child| child.kind().is_error())
|
||||
}
|
||||
|
||||
/// Consume the next token and turn it into an error.
|
||||
fn convert_to_error(&mut self, message: EcoString) {
|
||||
let kind = self.current;
|
||||
let offset = self.nodes.len();
|
||||
self.eat();
|
||||
self.balanced &= !kind.is_grouping();
|
||||
if !kind.is_error() {
|
||||
self.nodes[offset].convert_to_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a hint to the last node, if the last node is an error.
|
||||
fn hint(&mut self, hint: impl Into<EcoString>) {
|
||||
self.unskip();
|
||||
if let Some(last) = self.nodes.last_mut() {
|
||||
@ -1622,32 +1676,4 @@ impl<'s> Parser<'s> {
|
||||
}
|
||||
self.skip();
|
||||
}
|
||||
|
||||
fn expected_at(&mut self, m: Marker, thing: &str) {
|
||||
let message = eco_format!("expected {}", thing);
|
||||
let error = SyntaxNode::error(message, "");
|
||||
self.nodes.insert(m.0, error);
|
||||
}
|
||||
|
||||
fn unexpected(&mut self) {
|
||||
self.unskip();
|
||||
while self
|
||||
.nodes
|
||||
.last()
|
||||
.map_or(false, |child| child.kind() == SyntaxKind::Error && child.is_empty())
|
||||
{
|
||||
self.nodes.pop();
|
||||
}
|
||||
self.skip();
|
||||
|
||||
let kind = self.current;
|
||||
let offset = self.nodes.len();
|
||||
self.eat();
|
||||
self.balanced &= !kind.is_grouping();
|
||||
|
||||
if !kind.is_error() {
|
||||
self.nodes[offset]
|
||||
.convert_to_error(eco_format!("unexpected {}", kind.name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
tests/typ/compiler/embedded-expr.typ
Normal file
12
tests/typ/compiler/embedded-expr.typ
Normal file
@ -0,0 +1,12 @@
|
||||
// Test embedded expressions.
|
||||
// Ref: false
|
||||
|
||||
---
|
||||
// Error: 6-8 expected identifier, found keyword `as`
|
||||
#let as = 1 + 2
|
||||
|
||||
---
|
||||
#{
|
||||
// Error: 7-9 expected identifier, found keyword `as`
|
||||
let as = 10
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user