mirror of
https://github.com/typst/typst
synced 2025-05-21 20:45:27 +08:00
Code Review: Life is Like a Box of Iterators
This commit is contained in:
parent
7d34a548cc
commit
49fb3cd4e2
@ -6,7 +6,7 @@ use typst::eval::eval;
|
|||||||
use typst::layout::layout;
|
use typst::layout::layout;
|
||||||
use typst::loading::MemLoader;
|
use typst::loading::MemLoader;
|
||||||
use typst::parse::{parse, Scanner, TokenMode, Tokens};
|
use typst::parse::{parse, Scanner, TokenMode, Tokens};
|
||||||
use typst::source::{SourceFile, SourceId};
|
use typst::source::SourceId;
|
||||||
use typst::Context;
|
use typst::Context;
|
||||||
|
|
||||||
const SRC: &str = include_str!("bench.typ");
|
const SRC: &str = include_str!("bench.typ");
|
||||||
@ -44,13 +44,11 @@ fn bench_scan(iai: &mut Iai) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn bench_tokenize(iai: &mut Iai) {
|
fn bench_tokenize(iai: &mut Iai) {
|
||||||
let src = SourceFile::detached(SRC);
|
iai.run(|| Tokens::new(black_box(&SRC), black_box(TokenMode::Markup)).count());
|
||||||
iai.run(|| Tokens::new(black_box(&src), black_box(TokenMode::Markup)).count());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_parse(iai: &mut Iai) {
|
fn bench_parse(iai: &mut Iai) {
|
||||||
let src = SourceFile::detached(SRC);
|
iai.run(|| parse(&SRC));
|
||||||
iai.run(|| parse(&src));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_eval(iai: &mut Iai) {
|
fn bench_eval(iai: &mut Iai) {
|
||||||
|
@ -12,12 +12,11 @@ pub use tokens::*;
|
|||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::source::SourceFile;
|
|
||||||
use crate::syntax::*;
|
use crate::syntax::*;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// Parse a source file.
|
/// Parse a source file.
|
||||||
pub fn parse(source: &SourceFile) -> Rc<GreenNode> {
|
pub fn parse(source: &str) -> Rc<GreenNode> {
|
||||||
let mut p = Parser::new(source);
|
let mut p = Parser::new(source);
|
||||||
markup(&mut p);
|
markup(&mut p);
|
||||||
p.finish()
|
p.finish()
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{TokenMode, Tokens};
|
use super::{is_newline, TokenMode, Tokens};
|
||||||
use crate::source::{SourceFile, SourceId};
|
|
||||||
use crate::syntax::{ErrorPosition, Green, GreenData, GreenNode, NodeKind};
|
use crate::syntax::{ErrorPosition, Green, GreenData, GreenNode, NodeKind};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// A convenient token-based parser.
|
/// A convenient token-based parser.
|
||||||
pub struct Parser<'s> {
|
pub struct Parser<'s> {
|
||||||
/// The parsed file.
|
/// The parsed file.
|
||||||
source: &'s SourceFile,
|
src: &'s str,
|
||||||
/// An iterator over the source tokens.
|
/// An iterator over the source tokens.
|
||||||
tokens: Tokens<'s>,
|
tokens: Tokens<'s>,
|
||||||
/// The stack of open groups.
|
/// The stack of open groups.
|
||||||
@ -61,11 +60,11 @@ pub enum Group {
|
|||||||
|
|
||||||
impl<'s> Parser<'s> {
|
impl<'s> Parser<'s> {
|
||||||
/// Create a new parser for the source string.
|
/// Create a new parser for the source string.
|
||||||
pub fn new(source: &'s SourceFile) -> Self {
|
pub fn new(src: &'s str) -> Self {
|
||||||
let mut tokens = Tokens::new(source, TokenMode::Markup);
|
let mut tokens = Tokens::new(src, TokenMode::Markup);
|
||||||
let next = tokens.next();
|
let next = tokens.next();
|
||||||
Self {
|
Self {
|
||||||
source,
|
src,
|
||||||
tokens,
|
tokens,
|
||||||
groups: vec![],
|
groups: vec![],
|
||||||
next: next.clone(),
|
next: next.clone(),
|
||||||
@ -78,11 +77,6 @@ impl<'s> Parser<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The id of the parsed source file.
|
|
||||||
pub fn id(&self) -> SourceId {
|
|
||||||
self.source.id()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start a nested node.
|
/// Start a nested node.
|
||||||
///
|
///
|
||||||
/// Each start call has to be matched with a call to `end`,
|
/// Each start call has to be matched with a call to `end`,
|
||||||
@ -366,12 +360,16 @@ impl<'s> Parser<'s> {
|
|||||||
|
|
||||||
/// Determine the column index for the given byte index.
|
/// Determine the column index for the given byte index.
|
||||||
pub fn column(&self, index: usize) -> usize {
|
pub fn column(&self, index: usize) -> usize {
|
||||||
self.source.byte_to_column(index).unwrap()
|
self.src[.. index]
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.take_while(|&c| !is_newline(c))
|
||||||
|
.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Slice out part of the source string.
|
/// Slice out part of the source string.
|
||||||
pub fn get(&self, range: Range<usize>) -> &'s str {
|
pub fn get(&self, range: Range<usize>) -> &'s str {
|
||||||
self.source.get(range).unwrap()
|
self.src.get(range).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Continue parsing in a group.
|
/// Continue parsing in a group.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{is_newline, Scanner};
|
use super::{is_newline, Scanner};
|
||||||
use crate::syntax::RawToken;
|
use crate::syntax::RawData;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// Resolve all escape sequences in a string.
|
/// Resolve all escape sequences in a string.
|
||||||
@ -46,18 +46,18 @@ pub fn resolve_hex(sequence: &str) -> Option<char> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve the language tag and trims the raw text.
|
/// Resolve the language tag and trims the raw text.
|
||||||
pub fn resolve_raw(column: usize, backticks: u8, text: &str) -> RawToken {
|
pub fn resolve_raw(column: usize, backticks: u8, text: &str) -> RawData {
|
||||||
if backticks > 1 {
|
if backticks > 1 {
|
||||||
let (tag, inner) = split_at_lang_tag(text);
|
let (tag, inner) = split_at_lang_tag(text);
|
||||||
let (text, block) = trim_and_split_raw(column, inner);
|
let (text, block) = trim_and_split_raw(column, inner);
|
||||||
RawToken {
|
RawData {
|
||||||
lang: Some(tag.into()),
|
lang: Some(tag.into()),
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
backticks,
|
backticks,
|
||||||
block,
|
block,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RawToken {
|
RawData {
|
||||||
lang: None,
|
lang: None,
|
||||||
text: split_lines(text).join("\n").into(),
|
text: split_lines(text).join("\n").into(),
|
||||||
backticks,
|
backticks,
|
||||||
|
@ -106,6 +106,16 @@ impl<'s> Scanner<'s> {
|
|||||||
self.index
|
self.index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The column index of a given index in the source string.
|
||||||
|
#[inline]
|
||||||
|
pub fn column(&self, index: usize) -> usize {
|
||||||
|
self.src[.. index]
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.take_while(|&c| !is_newline(c))
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
|
||||||
/// Jump to an index in the source string.
|
/// Jump to an index in the source string.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn jump(&mut self, index: usize) {
|
pub fn jump(&mut self, index: usize) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use super::{is_newline, resolve_raw, Scanner};
|
use super::{is_newline, resolve_raw, Scanner};
|
||||||
use crate::geom::{AngularUnit, LengthUnit};
|
use crate::geom::{AngularUnit, LengthUnit};
|
||||||
use crate::parse::resolve::{resolve_hex, resolve_string};
|
use crate::parse::resolve::{resolve_hex, resolve_string};
|
||||||
use crate::source::SourceFile;
|
|
||||||
use crate::syntax::*;
|
use crate::syntax::*;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
@ -9,7 +8,6 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
/// An iterator over the tokens of a string of source code.
|
/// An iterator over the tokens of a string of source code.
|
||||||
pub struct Tokens<'s> {
|
pub struct Tokens<'s> {
|
||||||
source: &'s SourceFile,
|
|
||||||
s: Scanner<'s>,
|
s: Scanner<'s>,
|
||||||
mode: TokenMode,
|
mode: TokenMode,
|
||||||
}
|
}
|
||||||
@ -26,12 +24,8 @@ pub enum TokenMode {
|
|||||||
impl<'s> Tokens<'s> {
|
impl<'s> Tokens<'s> {
|
||||||
/// Create a new token iterator with the given mode.
|
/// Create a new token iterator with the given mode.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(source: &'s SourceFile, mode: TokenMode) -> Self {
|
pub fn new(source: &'s str, mode: TokenMode) -> Self {
|
||||||
Self {
|
Self { s: Scanner::new(source), mode }
|
||||||
s: Scanner::new(source.src()),
|
|
||||||
source,
|
|
||||||
mode,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current token mode.
|
/// Get the current token mode.
|
||||||
@ -244,7 +238,7 @@ impl<'s> Tokens<'s> {
|
|||||||
|
|
||||||
if self.s.eat_if('}') {
|
if self.s.eat_if('}') {
|
||||||
if let Some(character) = resolve_hex(&sequence) {
|
if let Some(character) = resolve_hex(&sequence) {
|
||||||
NodeKind::UnicodeEscape(UnicodeEscapeToken {
|
NodeKind::UnicodeEscape(UnicodeEscapeData {
|
||||||
character,
|
character,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -314,7 +308,7 @@ impl<'s> Tokens<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn raw(&mut self) -> NodeKind {
|
fn raw(&mut self) -> NodeKind {
|
||||||
let column = self.source.byte_to_column(self.s.index() - 1).unwrap();
|
let column = self.s.column(self.s.index() - 1);
|
||||||
let mut backticks = 1;
|
let mut backticks = 1;
|
||||||
while self.s.eat_if('`') && backticks < u8::MAX {
|
while self.s.eat_if('`') && backticks < u8::MAX {
|
||||||
backticks += 1;
|
backticks += 1;
|
||||||
@ -322,7 +316,7 @@ impl<'s> Tokens<'s> {
|
|||||||
|
|
||||||
// Special case for empty inline block.
|
// Special case for empty inline block.
|
||||||
if backticks == 2 {
|
if backticks == 2 {
|
||||||
return NodeKind::Raw(Rc::new(RawToken {
|
return NodeKind::Raw(Rc::new(RawData {
|
||||||
text: EcoString::new(),
|
text: EcoString::new(),
|
||||||
lang: None,
|
lang: None,
|
||||||
backticks: 1,
|
backticks: 1,
|
||||||
@ -397,7 +391,7 @@ impl<'s> Tokens<'s> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if terminated {
|
if terminated {
|
||||||
NodeKind::Math(Rc::new(MathToken {
|
NodeKind::Math(Rc::new(MathData {
|
||||||
formula: self.s.get(start .. end).into(),
|
formula: self.s.get(start .. end).into(),
|
||||||
display,
|
display,
|
||||||
}))
|
}))
|
||||||
@ -492,7 +486,7 @@ impl<'s> Tokens<'s> {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
if self.s.eat_if('"') {
|
if self.s.eat_if('"') {
|
||||||
NodeKind::Str(StrToken { string })
|
NodeKind::Str(StrData { string })
|
||||||
} else {
|
} else {
|
||||||
NodeKind::Error(ErrorPosition::End, "expected quote".into())
|
NodeKind::Error(ErrorPosition::End, "expected quote".into())
|
||||||
}
|
}
|
||||||
@ -567,7 +561,7 @@ mod tests {
|
|||||||
use TokenMode::{Code, Markup};
|
use TokenMode::{Code, Markup};
|
||||||
|
|
||||||
fn UnicodeEscape(character: char) -> NodeKind {
|
fn UnicodeEscape(character: char) -> NodeKind {
|
||||||
NodeKind::UnicodeEscape(UnicodeEscapeToken { character })
|
NodeKind::UnicodeEscape(UnicodeEscapeData { character })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Error(pos: ErrorPosition, message: &str) -> NodeKind {
|
fn Error(pos: ErrorPosition, message: &str) -> NodeKind {
|
||||||
@ -575,7 +569,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn Raw(text: &str, lang: Option<&str>, backticks_left: u8, block: bool) -> NodeKind {
|
fn Raw(text: &str, lang: Option<&str>, backticks_left: u8, block: bool) -> NodeKind {
|
||||||
NodeKind::Raw(Rc::new(RawToken {
|
NodeKind::Raw(Rc::new(RawData {
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
lang: lang.map(Into::into),
|
lang: lang.map(Into::into),
|
||||||
backticks: backticks_left,
|
backticks: backticks_left,
|
||||||
@ -586,7 +580,7 @@ mod tests {
|
|||||||
fn Math(formula: &str, display: bool, err_msg: Option<&str>) -> NodeKind {
|
fn Math(formula: &str, display: bool, err_msg: Option<&str>) -> NodeKind {
|
||||||
match err_msg {
|
match err_msg {
|
||||||
None => {
|
None => {
|
||||||
NodeKind::Math(Rc::new(MathToken { formula: formula.into(), display }))
|
NodeKind::Math(Rc::new(MathData { formula: formula.into(), display }))
|
||||||
}
|
}
|
||||||
Some(msg) => NodeKind::Error(
|
Some(msg) => NodeKind::Error(
|
||||||
ErrorPosition::End,
|
ErrorPosition::End,
|
||||||
@ -597,7 +591,7 @@ mod tests {
|
|||||||
|
|
||||||
fn Str(string: &str, terminated: bool) -> NodeKind {
|
fn Str(string: &str, terminated: bool) -> NodeKind {
|
||||||
if terminated {
|
if terminated {
|
||||||
NodeKind::Str(StrToken { string: string.into() })
|
NodeKind::Str(StrData { string: string.into() })
|
||||||
} else {
|
} else {
|
||||||
NodeKind::Error(ErrorPosition::End, "expected quote".into())
|
NodeKind::Error(ErrorPosition::End, "expected quote".into())
|
||||||
}
|
}
|
||||||
@ -687,7 +681,7 @@ mod tests {
|
|||||||
}};
|
}};
|
||||||
(@$mode:ident: $src:expr => $($token:expr),*) => {{
|
(@$mode:ident: $src:expr => $($token:expr),*) => {{
|
||||||
let src = $src;
|
let src = $src;
|
||||||
let found = Tokens::new(&SourceFile::detached(src.clone()), $mode).collect::<Vec<_>>();
|
let found = Tokens::new(&src, $mode).collect::<Vec<_>>();
|
||||||
let expected = vec![$($token.clone()),*];
|
let expected = vec![$($token.clone()),*];
|
||||||
check(&src, found, expected);
|
check(&src, found, expected);
|
||||||
}};
|
}};
|
||||||
|
@ -8,10 +8,10 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::diag::{Error, TypResult};
|
use crate::diag::TypResult;
|
||||||
use crate::loading::{FileHash, Loader};
|
use crate::loading::{FileHash, Loader};
|
||||||
use crate::parse::{is_newline, parse, Scanner};
|
use crate::parse::{is_newline, parse, Scanner};
|
||||||
use crate::syntax::{GreenNode, Markup, NodeKind, RedNode};
|
use crate::syntax::{GreenNode, Markup, RedNode};
|
||||||
use crate::util::PathExt;
|
use crate::util::PathExt;
|
||||||
|
|
||||||
#[cfg(feature = "codespan-reporting")]
|
#[cfg(feature = "codespan-reporting")]
|
||||||
@ -134,28 +134,22 @@ impl SourceFile {
|
|||||||
pub fn new(id: SourceId, path: &Path, src: String) -> Self {
|
pub fn new(id: SourceId, path: &Path, src: String) -> Self {
|
||||||
let mut line_starts = vec![0];
|
let mut line_starts = vec![0];
|
||||||
line_starts.extend(newlines(&src));
|
line_starts.extend(newlines(&src));
|
||||||
let mut init = Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
path: path.normalize(),
|
path: path.normalize(),
|
||||||
|
root: parse(&src),
|
||||||
src,
|
src,
|
||||||
line_starts,
|
line_starts,
|
||||||
root: Rc::new(GreenNode::new(NodeKind::Markup, 0)),
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let root = parse(&init);
|
|
||||||
init.root = root;
|
|
||||||
init
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ast(&self) -> TypResult<Markup> {
|
pub fn ast(&self) -> TypResult<Markup> {
|
||||||
let red = RedNode::new_root(self.root.clone(), self.id);
|
let red = RedNode::new_root(self.root.clone(), self.id);
|
||||||
let errors = red.errors();
|
let errors = red.errors();
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
Ok(red.as_ref().cast().unwrap())
|
Ok(red.cast().unwrap())
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(
|
Err(Box::new(errors))
|
||||||
errors.into_iter().map(|(span, msg)| Error::new(span, msg)).collect(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,39 @@
|
|||||||
use super::{Ident, NodeKind, RedNode, RedRef, Span, TypedNode};
|
use super::{Ident, NodeKind, RedNode, RedRef, Span, TypedNode};
|
||||||
use crate::geom::{AngularUnit, LengthUnit};
|
use crate::geom::{AngularUnit, LengthUnit};
|
||||||
use crate::node;
|
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
|
macro_rules! node {
|
||||||
|
($(#[$attr:meta])* $name:ident) => {
|
||||||
|
node!{$(#[$attr])* $name => $name}
|
||||||
|
};
|
||||||
|
($(#[$attr:meta])* $variant:ident => $name:ident) => {
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
$(#[$attr])*
|
||||||
|
pub struct $name(RedNode);
|
||||||
|
|
||||||
|
impl TypedNode for $name {
|
||||||
|
fn cast_from(node: RedRef) -> Option<Self> {
|
||||||
|
if node.kind() != &NodeKind::$variant {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Self(node.own()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
pub fn span(&self) -> Span {
|
||||||
|
self.0.span()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn underlying(&self) -> RedRef {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// The syntactical root capable of representing a full parsed document.
|
/// The syntactical root capable of representing a full parsed document.
|
||||||
Markup
|
Markup
|
||||||
|
@ -15,6 +15,7 @@ pub use ident::*;
|
|||||||
pub use pretty::*;
|
pub use pretty::*;
|
||||||
pub use span::*;
|
pub use span::*;
|
||||||
|
|
||||||
|
use crate::diag::Error;
|
||||||
use crate::geom::{AngularUnit, LengthUnit};
|
use crate::geom::{AngularUnit, LengthUnit};
|
||||||
use crate::source::SourceId;
|
use crate::source::SourceId;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
@ -94,9 +95,9 @@ impl GreenNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_children(kind: NodeKind, len: usize, children: Vec<Green>) -> Self {
|
pub fn with_children(kind: NodeKind, len: usize, children: Vec<Green>) -> Self {
|
||||||
let mut meta = GreenData::new(kind, len);
|
let mut data = GreenData::new(kind, len);
|
||||||
meta.erroneous |= children.iter().any(|c| c.erroneous());
|
data.erroneous |= children.iter().any(|c| c.erroneous());
|
||||||
Self { data: meta, children }
|
Self { data, children }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_child(kind: NodeKind, len: usize, child: impl Into<Green>) -> Self {
|
pub fn with_child(kind: NodeKind, len: usize, child: impl Into<Green>) -> Self {
|
||||||
@ -180,6 +181,10 @@ impl<'a> RedRef<'a> {
|
|||||||
Span::new(self.id, self.offset, self.offset + self.green.len())
|
Span::new(self.id, self.offset, self.offset + self.green.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.green.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cast<T>(self) -> Option<T>
|
pub fn cast<T>(self) -> Option<T>
|
||||||
where
|
where
|
||||||
T: TypedNode,
|
T: TypedNode,
|
||||||
@ -205,6 +210,29 @@ impl<'a> RedRef<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn errors(&self) -> Vec<Error> {
|
||||||
|
if !self.green.erroneous() {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.kind() {
|
||||||
|
NodeKind::Error(pos, msg) => {
|
||||||
|
let span = match pos {
|
||||||
|
ErrorPosition::Start => self.span().at_start(),
|
||||||
|
ErrorPosition::Full => self.span(),
|
||||||
|
ErrorPosition::End => self.span().at_end(),
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![Error::new(span, msg.to_string())]
|
||||||
|
}
|
||||||
|
_ => self
|
||||||
|
.children()
|
||||||
|
.filter(|red| red.green.erroneous())
|
||||||
|
.flat_map(|red| red.errors())
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option<RedRef> {
|
pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option<RedRef> {
|
||||||
self.children()
|
self.children()
|
||||||
.find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind))
|
.find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind))
|
||||||
@ -219,6 +247,18 @@ impl<'a> RedRef<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for RedRef<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}: {:?}", self.kind(), self.span())?;
|
||||||
|
let mut children = self.children().peekable();
|
||||||
|
if children.peek().is_some() {
|
||||||
|
f.write_str(" ")?;
|
||||||
|
f.debug_list().entries(children.map(RedRef::own)).finish()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct RedNode {
|
pub struct RedNode {
|
||||||
id: SourceId,
|
id: SourceId,
|
||||||
@ -231,12 +271,27 @@ impl RedNode {
|
|||||||
Self { id, offset: 0, green: root.into() }
|
Self { id, offset: 0, green: root.into() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_ref<'a>(&'a self) -> RedRef<'a> {
|
||||||
|
RedRef {
|
||||||
|
id: self.id,
|
||||||
|
offset: self.offset,
|
||||||
|
green: &self.green,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn span(&self) -> Span {
|
pub fn span(&self) -> Span {
|
||||||
self.as_ref().span()
|
self.as_ref().span()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.green.len()
|
self.as_ref().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cast<T>(self) -> Option<T>
|
||||||
|
where
|
||||||
|
T: TypedNode,
|
||||||
|
{
|
||||||
|
T::cast_from(self.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> &NodeKind {
|
pub fn kind(&self) -> &NodeKind {
|
||||||
@ -247,36 +302,8 @@ impl RedNode {
|
|||||||
self.as_ref().children()
|
self.as_ref().children()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errors(&self) -> Vec<(Span, EcoString)> {
|
pub fn errors<'a>(&'a self) -> Vec<Error> {
|
||||||
if !self.green.erroneous() {
|
self.as_ref().errors()
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.kind() {
|
|
||||||
NodeKind::Error(pos, msg) => {
|
|
||||||
let span = match pos {
|
|
||||||
ErrorPosition::Start => self.span().at_start(),
|
|
||||||
ErrorPosition::Full => self.span(),
|
|
||||||
ErrorPosition::End => self.span().at_end(),
|
|
||||||
};
|
|
||||||
|
|
||||||
vec![(span, msg.clone())]
|
|
||||||
}
|
|
||||||
_ => self
|
|
||||||
.as_ref()
|
|
||||||
.children()
|
|
||||||
.filter(|red| red.green.erroneous())
|
|
||||||
.flat_map(|red| red.own().errors())
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_ref<'a>(&'a self) -> RedRef<'a> {
|
|
||||||
RedRef {
|
|
||||||
id: self.id,
|
|
||||||
offset: self.offset,
|
|
||||||
green: &self.green,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option<RedNode> {
|
pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option<RedNode> {
|
||||||
@ -294,15 +321,7 @@ impl RedNode {
|
|||||||
|
|
||||||
impl Debug for RedNode {
|
impl Debug for RedNode {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "{:?}: {:?}", self.kind(), self.span())?;
|
self.as_ref().fmt(f)
|
||||||
let children = self.as_ref().children().collect::<Vec<_>>();
|
|
||||||
if !children.is_empty() {
|
|
||||||
f.write_str(" ")?;
|
|
||||||
f.debug_list()
|
|
||||||
.entries(children.into_iter().map(RedRef::own))
|
|
||||||
.finish()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,7 +438,7 @@ pub enum NodeKind {
|
|||||||
EmDash,
|
EmDash,
|
||||||
/// A slash and the letter "u" followed by a hexadecimal unicode entity
|
/// A slash and the letter "u" followed by a hexadecimal unicode entity
|
||||||
/// enclosed in curly braces: `\u{1F5FA}`.
|
/// enclosed in curly braces: `\u{1F5FA}`.
|
||||||
UnicodeEscape(UnicodeEscapeToken),
|
UnicodeEscape(UnicodeEscapeData),
|
||||||
/// Strong text was enabled / disabled: `*`.
|
/// Strong text was enabled / disabled: `*`.
|
||||||
Strong,
|
Strong,
|
||||||
/// Emphasized text was enabled / disabled: `_`.
|
/// Emphasized text was enabled / disabled: `_`.
|
||||||
@ -440,9 +459,9 @@ pub enum NodeKind {
|
|||||||
ListBullet,
|
ListBullet,
|
||||||
/// An arbitrary number of backticks followed by inner contents, terminated
|
/// An arbitrary number of backticks followed by inner contents, terminated
|
||||||
/// with the same number of backticks: `` `...` ``.
|
/// with the same number of backticks: `` `...` ``.
|
||||||
Raw(Rc<RawToken>),
|
Raw(Rc<RawData>),
|
||||||
/// Dollar signs surrounding inner contents.
|
/// Dollar signs surrounding inner contents.
|
||||||
Math(Rc<MathToken>),
|
Math(Rc<MathData>),
|
||||||
/// An identifier: `center`.
|
/// An identifier: `center`.
|
||||||
Ident(EcoString),
|
Ident(EcoString),
|
||||||
/// A boolean: `true`, `false`.
|
/// A boolean: `true`, `false`.
|
||||||
@ -463,7 +482,7 @@ pub enum NodeKind {
|
|||||||
/// A fraction unit: `3fr`.
|
/// A fraction unit: `3fr`.
|
||||||
Fraction(f64),
|
Fraction(f64),
|
||||||
/// A quoted string: `"..."`.
|
/// A quoted string: `"..."`.
|
||||||
Str(StrToken),
|
Str(StrData),
|
||||||
/// An array expression: `(1, "hi", 12cm)`.
|
/// An array expression: `(1, "hi", 12cm)`.
|
||||||
Array,
|
Array,
|
||||||
/// A dictionary expression: `(thickness: 3pt, pattern: dashed)`.
|
/// A dictionary expression: `(thickness: 3pt, pattern: dashed)`.
|
||||||
@ -534,15 +553,14 @@ pub enum ErrorPosition {
|
|||||||
|
|
||||||
/// A quoted string token: `"..."`.
|
/// A quoted string token: `"..."`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[repr(transparent)]
|
pub struct StrData {
|
||||||
pub struct StrToken {
|
|
||||||
/// The string inside the quotes.
|
/// The string inside the quotes.
|
||||||
pub string: EcoString,
|
pub string: EcoString,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A raw block token: `` `...` ``.
|
/// A raw block token: `` `...` ``.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct RawToken {
|
pub struct RawData {
|
||||||
/// The raw text in the block.
|
/// The raw text in the block.
|
||||||
pub text: EcoString,
|
pub text: EcoString,
|
||||||
/// The programming language of the raw text.
|
/// The programming language of the raw text.
|
||||||
@ -555,7 +573,7 @@ pub struct RawToken {
|
|||||||
|
|
||||||
/// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`.
|
/// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct MathToken {
|
pub struct MathData {
|
||||||
/// The formula between the dollars.
|
/// The formula between the dollars.
|
||||||
pub formula: EcoString,
|
pub formula: EcoString,
|
||||||
/// Whether the formula is display-level, that is, it is surrounded by
|
/// Whether the formula is display-level, that is, it is surrounded by
|
||||||
@ -565,8 +583,7 @@ pub struct MathToken {
|
|||||||
|
|
||||||
/// A unicode escape sequence token: `\u{1F5FA}`.
|
/// A unicode escape sequence token: `\u{1F5FA}`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[repr(transparent)]
|
pub struct UnicodeEscapeData {
|
||||||
pub struct UnicodeEscapeToken {
|
|
||||||
/// The resulting unicode character.
|
/// The resulting unicode character.
|
||||||
pub character: char,
|
pub character: char,
|
||||||
}
|
}
|
||||||
@ -712,36 +729,3 @@ impl NodeKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! node {
|
|
||||||
($(#[$attr:meta])* $name:ident) => {
|
|
||||||
node!{$(#[$attr])* $name => $name}
|
|
||||||
};
|
|
||||||
($(#[$attr:meta])* $variant:ident => $name:ident) => {
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
$(#[$attr])*
|
|
||||||
pub struct $name(RedNode);
|
|
||||||
|
|
||||||
impl TypedNode for $name {
|
|
||||||
fn cast_from(node: RedRef) -> Option<Self> {
|
|
||||||
if node.kind() != &NodeKind::$variant {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Self(node.own()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $name {
|
|
||||||
pub fn span(&self) -> Span {
|
|
||||||
self.0.span()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn underlying(&self) -> RedRef {
|
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user