mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +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
|
/// Whether this kind of node is automatically skipped by the parser in
|
||||||
/// code and math mode.
|
/// code and math mode.
|
||||||
pub fn is_trivia(self) -> bool {
|
pub fn is_trivia(self) -> bool {
|
||||||
|
@ -1583,11 +1583,16 @@ impl<'s> Parser<'s> {
|
|||||||
self.current = SyntaxKind::Eof;
|
self.current = SyntaxKind::Eof;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> Parser<'s> {
|
||||||
|
/// Consume the given syntax `kind` or produce an error.
|
||||||
fn expect(&mut self, kind: SyntaxKind) -> bool {
|
fn expect(&mut self, kind: SyntaxKind) -> bool {
|
||||||
let at = self.at(kind);
|
let at = self.at(kind);
|
||||||
if at {
|
if at {
|
||||||
self.eat();
|
self.eat();
|
||||||
|
} else if kind == SyntaxKind::Ident && self.current.is_keyword() {
|
||||||
|
self.expected_found(kind.name(), self.current.name());
|
||||||
} else {
|
} else {
|
||||||
self.balanced &= !kind.is_grouping();
|
self.balanced &= !kind.is_grouping();
|
||||||
self.expected(kind.name());
|
self.expected(kind.name());
|
||||||
@ -1595,26 +1600,75 @@ impl<'s> Parser<'s> {
|
|||||||
at
|
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) {
|
fn expect_closing_delimiter(&mut self, open: Marker, kind: SyntaxKind) {
|
||||||
if !self.eat_if(kind) {
|
if !self.eat_if(kind) {
|
||||||
self.nodes[open.0].convert_to_error("unclosed delimiter");
|
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();
|
self.unskip();
|
||||||
if self
|
while self
|
||||||
.nodes
|
.nodes
|
||||||
.last()
|
.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.pop();
|
||||||
self.nodes.push(SyntaxNode::error(message, ""));
|
|
||||||
}
|
}
|
||||||
self.skip();
|
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>) {
|
fn hint(&mut self, hint: impl Into<EcoString>) {
|
||||||
self.unskip();
|
self.unskip();
|
||||||
if let Some(last) = self.nodes.last_mut() {
|
if let Some(last) = self.nodes.last_mut() {
|
||||||
@ -1622,32 +1676,4 @@ impl<'s> Parser<'s> {
|
|||||||
}
|
}
|
||||||
self.skip();
|
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