mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
parent
20b8d2c121
commit
ad4ef68a11
@ -4,7 +4,7 @@ use unicode_script::{Script, UnicodeScript};
|
|||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
|
|
||||||
use crate::SyntaxKind;
|
use crate::{SyntaxError, SyntaxKind};
|
||||||
|
|
||||||
/// Splits up a string of source code into tokens.
|
/// Splits up a string of source code into tokens.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -19,7 +19,7 @@ pub(super) struct Lexer<'s> {
|
|||||||
/// The state held by raw line lexing.
|
/// The state held by raw line lexing.
|
||||||
raw: Vec<(SyntaxKind, usize)>,
|
raw: Vec<(SyntaxKind, usize)>,
|
||||||
/// An error for the last token.
|
/// An error for the last token.
|
||||||
error: Option<EcoString>,
|
error: Option<SyntaxError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What kind of tokens to emit.
|
/// What kind of tokens to emit.
|
||||||
@ -75,7 +75,7 @@ impl<'s> Lexer<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Take out the last error, if any.
|
/// Take out the last error, if any.
|
||||||
pub fn take_error(&mut self) -> Option<EcoString> {
|
pub fn take_error(&mut self) -> Option<SyntaxError> {
|
||||||
self.error.take()
|
self.error.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,9 +83,16 @@ impl<'s> Lexer<'s> {
|
|||||||
impl Lexer<'_> {
|
impl Lexer<'_> {
|
||||||
/// Construct a full-positioned syntax error.
|
/// Construct a full-positioned syntax error.
|
||||||
fn error(&mut self, message: impl Into<EcoString>) -> SyntaxKind {
|
fn error(&mut self, message: impl Into<EcoString>) -> SyntaxKind {
|
||||||
self.error = Some(message.into());
|
self.error = Some(SyntaxError::new(message));
|
||||||
SyntaxKind::Error
|
SyntaxKind::Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the current node is an error, adds a hint.
|
||||||
|
fn hint(&mut self, message: impl Into<EcoString>) {
|
||||||
|
if let Some(error) = &mut self.error {
|
||||||
|
error.hints.push(message.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shared methods with all [`LexMode`].
|
/// Shared methods with all [`LexMode`].
|
||||||
@ -109,7 +116,12 @@ impl Lexer<'_> {
|
|||||||
Some('/') if self.s.eat_if('/') => self.line_comment(),
|
Some('/') if self.s.eat_if('/') => self.line_comment(),
|
||||||
Some('/') if self.s.eat_if('*') => self.block_comment(),
|
Some('/') if self.s.eat_if('*') => self.block_comment(),
|
||||||
Some('*') if self.s.eat_if('/') => {
|
Some('*') if self.s.eat_if('/') => {
|
||||||
self.error("unexpected end of block comment")
|
let kind = self.error("unexpected end of block comment");
|
||||||
|
self.hint(
|
||||||
|
"consider escaping the `*` with a backslash or \
|
||||||
|
opening the block comment with `/*`",
|
||||||
|
);
|
||||||
|
kind
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(c) => match self.mode {
|
Some(c) => match self.mode {
|
||||||
|
@ -35,8 +35,8 @@ impl SyntaxNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new error node.
|
/// Create a new error node.
|
||||||
pub fn error(message: impl Into<EcoString>, text: impl Into<EcoString>) -> Self {
|
pub fn error(error: SyntaxError, text: impl Into<EcoString>) -> Self {
|
||||||
Self(Repr::Error(Arc::new(ErrorNode::new(message, text))))
|
Self(Repr::Error(Arc::new(ErrorNode::new(error, text))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a dummy node of the given kind.
|
/// Create a dummy node of the given kind.
|
||||||
@ -209,7 +209,7 @@ impl SyntaxNode {
|
|||||||
pub(super) fn convert_to_error(&mut self, message: impl Into<EcoString>) {
|
pub(super) fn convert_to_error(&mut self, message: impl Into<EcoString>) {
|
||||||
if !self.kind().is_error() {
|
if !self.kind().is_error() {
|
||||||
let text = std::mem::take(self).into_text();
|
let text = std::mem::take(self).into_text();
|
||||||
*self = SyntaxNode::error(message, text);
|
*self = SyntaxNode::error(SyntaxError::new(message), text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,15 +628,8 @@ struct ErrorNode {
|
|||||||
|
|
||||||
impl ErrorNode {
|
impl ErrorNode {
|
||||||
/// Create new error node.
|
/// Create new error node.
|
||||||
fn new(message: impl Into<EcoString>, text: impl Into<EcoString>) -> Self {
|
fn new(error: SyntaxError, text: impl Into<EcoString>) -> Self {
|
||||||
Self {
|
Self { text: text.into(), error }
|
||||||
text: text.into(),
|
|
||||||
error: SyntaxError {
|
|
||||||
span: Span::detached(),
|
|
||||||
message: message.into(),
|
|
||||||
hints: eco_vec![],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The byte length of the node in the source text.
|
/// The byte length of the node in the source text.
|
||||||
@ -674,6 +667,15 @@ pub struct SyntaxError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxError {
|
impl SyntaxError {
|
||||||
|
/// Create a new detached syntax error.
|
||||||
|
pub fn new(message: impl Into<EcoString>) -> Self {
|
||||||
|
Self {
|
||||||
|
span: Span::detached(),
|
||||||
|
message: message.into(),
|
||||||
|
hints: eco_vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the two errors are the same apart from spans.
|
/// Whether the two errors are the same apart from spans.
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
fn spanless_eq(&self, other: &Self) -> bool {
|
||||||
self.message == other.message && self.hints == other.hints
|
self.message == other.message && self.hints == other.hints
|
||||||
|
@ -6,7 +6,9 @@ use ecow::{eco_format, EcoString};
|
|||||||
use unicode_math_class::MathClass;
|
use unicode_math_class::MathClass;
|
||||||
|
|
||||||
use crate::set::SyntaxSet;
|
use crate::set::SyntaxSet;
|
||||||
use crate::{ast, is_ident, is_newline, set, LexMode, Lexer, SyntaxKind, SyntaxNode};
|
use crate::{
|
||||||
|
ast, is_ident, is_newline, set, LexMode, Lexer, SyntaxError, SyntaxKind, SyntaxNode,
|
||||||
|
};
|
||||||
|
|
||||||
/// Parses a source file.
|
/// Parses a source file.
|
||||||
pub fn parse(text: &str) -> SyntaxNode {
|
pub fn parse(text: &str) -> SyntaxNode {
|
||||||
@ -1760,8 +1762,8 @@ impl<'s> Parser<'s> {
|
|||||||
fn save(&mut self) {
|
fn save(&mut self) {
|
||||||
let text = self.current_text();
|
let text = self.current_text();
|
||||||
if self.at(SyntaxKind::Error) {
|
if self.at(SyntaxKind::Error) {
|
||||||
let message = self.lexer.take_error().unwrap();
|
let error = self.lexer.take_error().unwrap();
|
||||||
self.nodes.push(SyntaxNode::error(message, text));
|
self.nodes.push(SyntaxNode::error(error, text));
|
||||||
} else {
|
} else {
|
||||||
self.nodes.push(SyntaxNode::leaf(self.current, text));
|
self.nodes.push(SyntaxNode::leaf(self.current, text));
|
||||||
}
|
}
|
||||||
@ -1838,7 +1840,8 @@ impl<'s> Parser<'s> {
|
|||||||
/// Produce an error that the given `thing` was expected at the position
|
/// Produce an error that the given `thing` was expected at the position
|
||||||
/// of the marker `m`.
|
/// of the marker `m`.
|
||||||
fn expected_at(&mut self, m: Marker, thing: &str) {
|
fn expected_at(&mut self, m: Marker, thing: &str) {
|
||||||
let error = SyntaxNode::error(eco_format!("expected {thing}"), "");
|
let error =
|
||||||
|
SyntaxNode::error(SyntaxError::new(eco_format!("expected {thing}")), "");
|
||||||
self.nodes.insert(m.0, error);
|
self.nodes.insert(m.0, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
--- array-bad-token ---
|
--- array-bad-token ---
|
||||||
// Error: 4-6 unexpected end of block comment
|
// Error: 4-6 unexpected end of block comment
|
||||||
|
// Hint: 4-6 consider escaping the `*` with a backslash or opening the block comment with `/*`
|
||||||
#(1*/2)
|
#(1*/2)
|
||||||
|
|
||||||
--- array-bad-number-suffix ---
|
--- array-bad-number-suffix ---
|
||||||
|
@ -80,6 +80,7 @@
|
|||||||
|
|
||||||
--- call-args-bad-token ---
|
--- call-args-bad-token ---
|
||||||
// Error: 10-12 unexpected end of block comment
|
// Error: 10-12 unexpected end of block comment
|
||||||
|
// Hint: 10-12 consider escaping the `*` with a backslash or opening the block comment with `/*`
|
||||||
#func(a:1*/)
|
#func(a:1*/)
|
||||||
|
|
||||||
--- call-args-missing-comma ---
|
--- call-args-missing-comma ---
|
||||||
|
@ -37,6 +37,7 @@ Second part
|
|||||||
--- comment-block-unclosed ---
|
--- comment-block-unclosed ---
|
||||||
// End should not appear without start.
|
// End should not appear without start.
|
||||||
// Error: 7-9 unexpected end of block comment
|
// Error: 7-9 unexpected end of block comment
|
||||||
|
// Hint: 7-9 consider escaping the `*` with a backslash or opening the block comment with `/*`
|
||||||
/* */ */
|
/* */ */
|
||||||
|
|
||||||
// Unterminated is okay.
|
// Unterminated is okay.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user