Refactoring
Co-Authored-By: Martin <mhaug@live.de>
This commit is contained in:
parent
42afb27cef
commit
65fac0e57c
@ -100,7 +100,7 @@ impl<T> Trace<T> for TypResult<T> {
|
||||
{
|
||||
self.map_err(|mut errors| {
|
||||
for error in errors.iter_mut() {
|
||||
if !span.contains(error.span) {
|
||||
if !span.surrounds(error.span) {
|
||||
error.trace.push(Spanned::new(make_point(), span));
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{Scope, Scopes, Value};
|
||||
use crate::syntax::{ClosureParam, Expr, Imports, RedRef};
|
||||
use crate::syntax::ast::{ClosureParam, Expr, Imports};
|
||||
use crate::syntax::RedRef;
|
||||
|
||||
/// A visitor that captures variable slots.
|
||||
pub struct CapturesVisitor<'a> {
|
||||
|
@ -36,7 +36,8 @@ use crate::geom::{Angle, Fractional, Length, Relative};
|
||||
use crate::image::ImageStore;
|
||||
use crate::loading::Loader;
|
||||
use crate::source::{SourceId, SourceStore};
|
||||
use crate::syntax::*;
|
||||
use crate::syntax::ast::*;
|
||||
use crate::syntax::{Span, Spanned};
|
||||
use crate::util::RefMutExt;
|
||||
use crate::Context;
|
||||
|
||||
@ -238,7 +239,7 @@ impl Eval for DictExpr {
|
||||
|
||||
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
|
||||
self.items()
|
||||
.map(|x| Ok(((&x.name().string).into(), x.expr().eval(ctx)?)))
|
||||
.map(|x| Ok((x.name().string.into(), x.expr().eval(ctx)?)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use crate::diag::TypResult;
|
||||
use crate::geom::Spec;
|
||||
use crate::layout::BlockLevel;
|
||||
use crate::library::{GridNode, ParChild, ParNode, TrackSizing};
|
||||
use crate::syntax::*;
|
||||
use crate::syntax::ast::*;
|
||||
use crate::util::BoolExt;
|
||||
|
||||
/// Walk markup, filling the currently built template.
|
||||
|
@ -3,7 +3,7 @@ use super::*;
|
||||
/// A relative length.
|
||||
///
|
||||
/// _Note_: `50%` is represented as `0.5` here, but stored as `50.0` in the
|
||||
/// corresponding [literal](crate::syntax::Lit::Percent).
|
||||
/// corresponding [literal](crate::syntax::ast::Lit::Percent).
|
||||
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct Relative(N64);
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
//!
|
||||
//! [tokens]: parse::Tokens
|
||||
//! [parsed]: parse::parse
|
||||
//! [markup]: syntax::Markup
|
||||
//! [markup]: syntax::ast::Markup
|
||||
//! [evaluate]: eval::eval
|
||||
//! [module]: eval::Module
|
||||
//! [layout tree]: layout::LayoutTree
|
||||
|
@ -12,7 +12,8 @@ pub use tokens::*;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::syntax::*;
|
||||
use crate::syntax::ast::{Associativity, BinOp, UnOp};
|
||||
use crate::syntax::{ErrorPosition, GreenNode, NodeKind};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// Parse a source file.
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{is_newline, TokenMode, Tokens};
|
||||
use super::{TokenMode, Tokens};
|
||||
use crate::syntax::{ErrorPosition, Green, GreenData, GreenNode, NodeKind};
|
||||
use crate::util::EcoString;
|
||||
|
||||
@ -375,11 +375,7 @@ impl<'s> Parser<'s> {
|
||||
|
||||
/// Determine the column index for the given byte index.
|
||||
pub fn column(&self, index: usize) -> usize {
|
||||
self.src[.. index]
|
||||
.chars()
|
||||
.rev()
|
||||
.take_while(|&c| !is_newline(c))
|
||||
.count()
|
||||
self.tokens.column(index)
|
||||
}
|
||||
|
||||
/// Slice out part of the source string.
|
||||
|
@ -1,5 +1,7 @@
|
||||
use std::slice::SliceIndex;
|
||||
|
||||
use unicode_xid::UnicodeXID;
|
||||
|
||||
/// A featureful char-based scanner.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Scanner<'s> {
|
||||
@ -106,16 +108,6 @@ impl<'s> Scanner<'s> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn jump(&mut self, index: usize) {
|
||||
@ -124,6 +116,12 @@ impl<'s> Scanner<'s> {
|
||||
self.index = index;
|
||||
}
|
||||
|
||||
/// The full source string.
|
||||
#[inline]
|
||||
pub fn src(&self) -> &'s str {
|
||||
&self.src
|
||||
}
|
||||
|
||||
/// Slice out part of the source string.
|
||||
#[inline]
|
||||
pub fn get<I>(&self, index: I) -> &'s str
|
||||
@ -160,6 +158,16 @@ impl<'s> Scanner<'s> {
|
||||
// optimized away in some cases.
|
||||
self.src.get(start .. self.index).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this character denotes a newline.
|
||||
@ -173,3 +181,24 @@ pub fn is_newline(character: char) -> bool {
|
||||
'\u{0085}' | '\u{2028}' | '\u{2029}'
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether a string is a valid identifier.
|
||||
#[inline]
|
||||
pub fn is_ident(string: &str) -> bool {
|
||||
let mut chars = string.chars();
|
||||
chars
|
||||
.next()
|
||||
.map_or(false, |c| is_id_start(c) && chars.all(is_id_continue))
|
||||
}
|
||||
|
||||
/// Whether a character can start an identifier.
|
||||
#[inline]
|
||||
pub fn is_id_start(c: char) -> bool {
|
||||
c.is_xid_start() || c == '_'
|
||||
}
|
||||
|
||||
/// Whether a character can continue an identifier.
|
||||
#[inline]
|
||||
pub fn is_id_continue(c: char) -> bool {
|
||||
c.is_xid_continue() || c == '_' || c == '-'
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
use super::{is_newline, resolve_raw, Scanner};
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{
|
||||
is_id_continue, is_id_start, is_newline, resolve_hex, resolve_raw, resolve_string,
|
||||
Scanner,
|
||||
};
|
||||
use crate::geom::{AngularUnit, LengthUnit};
|
||||
use crate::parse::resolve::{resolve_hex, resolve_string};
|
||||
use crate::syntax::*;
|
||||
use crate::util::EcoString;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
/// An iterator over the tokens of a string of source code.
|
||||
pub struct Tokens<'s> {
|
||||
s: Scanner<'s>,
|
||||
@ -55,6 +57,12 @@ impl<'s> Tokens<'s> {
|
||||
self.s.jump(index);
|
||||
}
|
||||
|
||||
/// The column of a given index in the source string.
|
||||
#[inline]
|
||||
pub fn column(&self, index: usize) -> usize {
|
||||
self.s.column(index)
|
||||
}
|
||||
|
||||
/// The underlying scanner.
|
||||
#[inline]
|
||||
pub fn scanner(&self) -> Scanner<'s> {
|
||||
@ -237,10 +245,8 @@ impl<'s> Tokens<'s> {
|
||||
let sequence: EcoString = self.s.eat_while(|c| c.is_ascii_alphanumeric()).into();
|
||||
|
||||
if self.s.eat_if('}') {
|
||||
if let Some(character) = resolve_hex(&sequence) {
|
||||
NodeKind::UnicodeEscape(UnicodeEscapeData {
|
||||
character,
|
||||
})
|
||||
if let Some(c) = resolve_hex(&sequence) {
|
||||
NodeKind::UnicodeEscape(c)
|
||||
} else {
|
||||
NodeKind::Error(
|
||||
ErrorPosition::Full,
|
||||
@ -308,7 +314,8 @@ impl<'s> Tokens<'s> {
|
||||
}
|
||||
|
||||
fn raw(&mut self) -> NodeKind {
|
||||
let column = self.s.column(self.s.index() - 1);
|
||||
let column = self.column(self.s.index() - 1);
|
||||
|
||||
let mut backticks = 1;
|
||||
while self.s.eat_if('`') && backticks < u8::MAX {
|
||||
backticks += 1;
|
||||
@ -486,7 +493,7 @@ impl<'s> Tokens<'s> {
|
||||
}
|
||||
}));
|
||||
if self.s.eat_if('"') {
|
||||
NodeKind::Str(StrData { string })
|
||||
NodeKind::Str(string)
|
||||
} else {
|
||||
NodeKind::Error(ErrorPosition::End, "expected quote".into())
|
||||
}
|
||||
@ -556,12 +563,13 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
use ErrorPosition::*;
|
||||
use NodeKind::*;
|
||||
use Option::None;
|
||||
use TokenMode::{Code, Markup};
|
||||
|
||||
fn UnicodeEscape(character: char) -> NodeKind {
|
||||
NodeKind::UnicodeEscape(UnicodeEscapeData { character })
|
||||
fn UnicodeEscape(c: char) -> NodeKind {
|
||||
NodeKind::UnicodeEscape(c)
|
||||
}
|
||||
|
||||
fn Error(pos: ErrorPosition, message: &str) -> NodeKind {
|
||||
@ -577,24 +585,12 @@ mod tests {
|
||||
}))
|
||||
}
|
||||
|
||||
fn Math(formula: &str, display: bool, err_msg: Option<&str>) -> NodeKind {
|
||||
match err_msg {
|
||||
None => {
|
||||
NodeKind::Math(Rc::new(MathData { formula: formula.into(), display }))
|
||||
}
|
||||
Some(msg) => NodeKind::Error(
|
||||
ErrorPosition::End,
|
||||
format!("expected closing {}", msg).into(),
|
||||
),
|
||||
}
|
||||
fn Math(formula: &str, display: bool) -> NodeKind {
|
||||
NodeKind::Math(Rc::new(MathData { formula: formula.into(), display }))
|
||||
}
|
||||
|
||||
fn Str(string: &str, terminated: bool) -> NodeKind {
|
||||
if terminated {
|
||||
NodeKind::Str(StrData { string: string.into() })
|
||||
} else {
|
||||
NodeKind::Error(ErrorPosition::End, "expected quote".into())
|
||||
}
|
||||
fn Str(string: &str) -> NodeKind {
|
||||
NodeKind::Str(string.into())
|
||||
}
|
||||
|
||||
fn Text(string: &str) -> NodeKind {
|
||||
@ -659,7 +655,7 @@ mod tests {
|
||||
('/', None, "//", LineComment),
|
||||
('/', None, "/**/", BlockComment),
|
||||
('/', Some(Markup), "*", Strong),
|
||||
('/', Some(Markup), "$ $", Math(" ", false, None)),
|
||||
('/', Some(Markup), "$ $", Math(" ", false)),
|
||||
('/', Some(Markup), r"\\", Text("\\")),
|
||||
('/', Some(Markup), "#let", Let),
|
||||
('/', Some(Code), "(", LeftParen),
|
||||
@ -781,16 +777,16 @@ mod tests {
|
||||
t!(Markup[" /"]: r#"\""# => Text(r"\"), Text("\""));
|
||||
|
||||
// Test basic unicode escapes.
|
||||
t!(Markup: r"\u{}" => Error(ErrorPosition::Full, "invalid unicode escape sequence"));
|
||||
t!(Markup: r"\u{}" => Error(Full, "invalid unicode escape sequence"));
|
||||
t!(Markup: r"\u{2603}" => UnicodeEscape('☃'));
|
||||
t!(Markup: r"\u{P}" => Error(ErrorPosition::Full, "invalid unicode escape sequence"));
|
||||
t!(Markup: r"\u{P}" => Error(Full, "invalid unicode escape sequence"));
|
||||
|
||||
// Test unclosed unicode escapes.
|
||||
t!(Markup[" /"]: r"\u{" => Error(ErrorPosition::End, "expected closing brace"));
|
||||
t!(Markup[" /"]: r"\u{1" => Error(ErrorPosition::End, "expected closing brace"));
|
||||
t!(Markup[" /"]: r"\u{26A4" => Error(ErrorPosition::End, "expected closing brace"));
|
||||
t!(Markup[" /"]: r"\u{1Q3P" => Error(ErrorPosition::End, "expected closing brace"));
|
||||
t!(Markup: r"\u{1🏕}" => Error(ErrorPosition::End, "expected closing brace"), Text("🏕"), RightBrace);
|
||||
t!(Markup[" /"]: r"\u{" => Error(End, "expected closing brace"));
|
||||
t!(Markup[" /"]: r"\u{1" => Error(End, "expected closing brace"));
|
||||
t!(Markup[" /"]: r"\u{26A4" => Error(End, "expected closing brace"));
|
||||
t!(Markup[" /"]: r"\u{1Q3P" => Error(End, "expected closing brace"));
|
||||
t!(Markup: r"\u{1🏕}" => Error(End, "expected closing brace"), Text("🏕"), RightBrace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -882,11 +878,11 @@ mod tests {
|
||||
// Test basic raw block.
|
||||
t!(Markup: "``" => Raw("", None, 1, false));
|
||||
t!(Markup: "`raw`" => Raw("raw", None, 1, false));
|
||||
t!(Markup[""]: "`]" => Error(ErrorPosition::End, "expected 1 backtick"));
|
||||
t!(Markup[""]: "`]" => Error(End, "expected 1 backtick"));
|
||||
|
||||
// Test special symbols in raw block.
|
||||
t!(Markup: "`[brackets]`" => Raw("[brackets]", None, 1, false));
|
||||
t!(Markup[""]: r"`\`` " => Raw(r"\", None, 1, false), Error(ErrorPosition::End, "expected 1 backtick"));
|
||||
t!(Markup[""]: r"`\`` " => Raw(r"\", None, 1, false), Error(End, "expected 1 backtick"));
|
||||
|
||||
// Test separated closing backticks.
|
||||
t!(Markup: "```not `y`e`t```" => Raw("`y`e`t", Some("not"), 3, false));
|
||||
@ -894,28 +890,28 @@ mod tests {
|
||||
// Test more backticks.
|
||||
t!(Markup: "``nope``" => Raw("", None, 1, false), Text("nope"), Raw("", None, 1, false));
|
||||
t!(Markup: "````🚀````" => Raw("", Some("🚀"), 4, false));
|
||||
t!(Markup[""]: "`````👩🚀````noend" => Error(ErrorPosition::End, "expected 5 backticks"));
|
||||
t!(Markup[""]: "`````👩🚀````noend" => Error(End, "expected 5 backticks"));
|
||||
t!(Markup[""]: "````raw``````" => Raw("", Some("raw"), 4, false), Raw("", None, 1, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tokenize_math_formulas() {
|
||||
// Test basic formula.
|
||||
t!(Markup: "$$" => Math("", false, None));
|
||||
t!(Markup: "$x$" => Math("x", false, None));
|
||||
t!(Markup: r"$\\$" => Math(r"\\", false, None));
|
||||
t!(Markup: "$[x + y]$" => Math("x + y", true, None));
|
||||
t!(Markup: r"$[\\]$" => Math(r"\\", true, None));
|
||||
t!(Markup: "$$" => Math("", false));
|
||||
t!(Markup: "$x$" => Math("x", false));
|
||||
t!(Markup: r"$\\$" => Math(r"\\", false));
|
||||
t!(Markup: "$[x + y]$" => Math("x + y", true));
|
||||
t!(Markup: r"$[\\]$" => Math(r"\\", true));
|
||||
|
||||
// Test unterminated.
|
||||
t!(Markup[""]: "$x" => Math("x", false, Some("dollar sign")));
|
||||
t!(Markup[""]: "$[x" => Math("x", true, Some("bracket and dollar sign")));
|
||||
t!(Markup[""]: "$[x]\n$" => Math("x]\n$", true, Some("bracket and dollar sign")));
|
||||
t!(Markup[""]: "$x" => Error(End, "expected closing dollar sign"));
|
||||
t!(Markup[""]: "$[x" => Error(End, "expected closing bracket and dollar sign"));
|
||||
t!(Markup[""]: "$[x]\n$" => Error(End, "expected closing bracket and dollar sign"));
|
||||
|
||||
// Test escape sequences.
|
||||
t!(Markup: r"$\$x$" => Math(r"\$x", false, None));
|
||||
t!(Markup: r"$[\\\]$]$" => Math(r"\\\]$", true, None));
|
||||
t!(Markup[""]: r"$[ ]\\$" => Math(r" ]\\$", true, Some("bracket and dollar sign")));
|
||||
t!(Markup: r"$\$x$" => Math(r"\$x", false));
|
||||
t!(Markup: r"$[\\\]$]$" => Math(r"\\\]$", true));
|
||||
t!(Markup[""]: r"$[ ]\\$" => Error(End, "expected closing bracket and dollar sign"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1003,16 +999,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_tokenize_strings() {
|
||||
// Test basic strings.
|
||||
t!(Code: "\"hi\"" => Str("hi", true));
|
||||
t!(Code: "\"hi\nthere\"" => Str("hi\nthere", true));
|
||||
t!(Code: "\"🌎\"" => Str("🌎", true));
|
||||
t!(Code: "\"hi\"" => Str("hi"));
|
||||
t!(Code: "\"hi\nthere\"" => Str("hi\nthere"));
|
||||
t!(Code: "\"🌎\"" => Str("🌎"));
|
||||
|
||||
// Test unterminated.
|
||||
t!(Code[""]: "\"hi" => Str("hi", false));
|
||||
t!(Code[""]: "\"hi" => Error(End, "expected quote"));
|
||||
|
||||
// Test escaped quote.
|
||||
t!(Code: r#""a\"bc""# => Str("a\"bc", true));
|
||||
t!(Code[""]: r#""\""# => Str("\"", false));
|
||||
t!(Code: r#""a\"bc""# => Str("a\"bc"));
|
||||
t!(Code[""]: r#""\""# => Error(End, "expected quote"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -11,7 +11,8 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::diag::TypResult;
|
||||
use crate::loading::{FileHash, Loader};
|
||||
use crate::parse::{is_newline, parse, Scanner};
|
||||
use crate::syntax::{GreenNode, Markup, RedNode};
|
||||
use crate::syntax::ast::Markup;
|
||||
use crate::syntax::{GreenNode, RedNode};
|
||||
use crate::util::PathExt;
|
||||
|
||||
#[cfg(feature = "codespan-reporting")]
|
||||
|
@ -1,7 +1,18 @@
|
||||
use super::{Ident, NodeKind, RedNode, RedRef, Span, TypedNode};
|
||||
//! A typed layer over the red-green tree.
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use super::{NodeKind, RedNode, RedRef, Span};
|
||||
use crate::geom::{AngularUnit, LengthUnit};
|
||||
use crate::parse::is_ident;
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A typed AST node.
|
||||
pub trait TypedNode: Sized {
|
||||
/// Convert from a red node to a typed node.
|
||||
fn from_red(value: RedRef) -> Option<Self>;
|
||||
}
|
||||
|
||||
macro_rules! node {
|
||||
($(#[$attr:meta])* $name:ident) => {
|
||||
node!{$(#[$attr])* $name => $name}
|
||||
@ -13,7 +24,7 @@ macro_rules! node {
|
||||
pub struct $name(RedNode);
|
||||
|
||||
impl TypedNode for $name {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
if node.kind() != &NodeKind::$variant {
|
||||
return None;
|
||||
}
|
||||
@ -23,10 +34,12 @@ macro_rules! node {
|
||||
}
|
||||
|
||||
impl $name {
|
||||
/// The source code location.
|
||||
pub fn span(&self) -> Span {
|
||||
self.0.span()
|
||||
}
|
||||
|
||||
/// The underlying red node.
|
||||
pub fn underlying(&self) -> RedRef {
|
||||
self.0.as_ref()
|
||||
}
|
||||
@ -40,7 +53,8 @@ node! {
|
||||
}
|
||||
|
||||
impl Markup {
|
||||
pub fn nodes<'a>(&'a self) -> impl Iterator<Item = MarkupNode> + 'a {
|
||||
/// The markup nodes.
|
||||
pub fn nodes(&self) -> impl Iterator<Item = MarkupNode> + '_ {
|
||||
self.0.children().filter_map(RedRef::cast)
|
||||
}
|
||||
}
|
||||
@ -73,7 +87,7 @@ pub enum MarkupNode {
|
||||
}
|
||||
|
||||
impl TypedNode for MarkupNode {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Space(_) => Some(MarkupNode::Space),
|
||||
NodeKind::Linebreak => Some(MarkupNode::Linebreak),
|
||||
@ -81,17 +95,14 @@ impl TypedNode for MarkupNode {
|
||||
NodeKind::Strong => Some(MarkupNode::Strong),
|
||||
NodeKind::Emph => Some(MarkupNode::Emph),
|
||||
NodeKind::Text(s) => Some(MarkupNode::Text(s.clone())),
|
||||
NodeKind::UnicodeEscape(u) => Some(MarkupNode::Text(u.character.into())),
|
||||
NodeKind::EnDash => Some(MarkupNode::Text(EcoString::from("\u{2013}"))),
|
||||
NodeKind::EmDash => Some(MarkupNode::Text(EcoString::from("\u{2014}"))),
|
||||
NodeKind::NonBreakingSpace => {
|
||||
Some(MarkupNode::Text(EcoString::from("\u{00A0}")))
|
||||
}
|
||||
NodeKind::UnicodeEscape(c) => Some(MarkupNode::Text((*c).into())),
|
||||
NodeKind::EnDash => Some(MarkupNode::Text("\u{2013}".into())),
|
||||
NodeKind::EmDash => Some(MarkupNode::Text("\u{2014}".into())),
|
||||
NodeKind::NonBreakingSpace => Some(MarkupNode::Text("\u{00A0}".into())),
|
||||
NodeKind::Raw(_) => node.cast().map(MarkupNode::Raw),
|
||||
NodeKind::Heading => node.cast().map(MarkupNode::Heading),
|
||||
NodeKind::List => node.cast().map(MarkupNode::List),
|
||||
NodeKind::Enum => node.cast().map(MarkupNode::Enum),
|
||||
NodeKind::Error(_, _) => None,
|
||||
_ => node.cast().map(MarkupNode::Expr),
|
||||
}
|
||||
}
|
||||
@ -111,16 +122,16 @@ pub struct RawNode {
|
||||
}
|
||||
|
||||
impl TypedNode for RawNode {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Raw(raw) => {
|
||||
let span = node.span();
|
||||
let start = span.start + raw.backticks as usize;
|
||||
let full = node.span();
|
||||
let start = full.start + raw.backticks as usize;
|
||||
Some(Self {
|
||||
block: raw.block,
|
||||
lang: raw.lang.as_ref().and_then(|x| {
|
||||
let span = Span::new(span.source, start, start + x.len());
|
||||
Ident::new(x, span)
|
||||
lang: raw.lang.as_ref().and_then(|lang| {
|
||||
let span = Span::new(full.source, start, start + lang.len());
|
||||
Ident::new(lang, span)
|
||||
}),
|
||||
text: raw.text.clone(),
|
||||
})
|
||||
@ -272,7 +283,7 @@ impl Expr {
|
||||
}
|
||||
|
||||
impl TypedNode for Expr {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Ident(_) => node.cast().map(Self::Ident),
|
||||
NodeKind::Array => node.cast().map(Self::Array),
|
||||
@ -325,7 +336,7 @@ pub enum Lit {
|
||||
}
|
||||
|
||||
impl TypedNode for Lit {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::None => Some(Self::None(node.span())),
|
||||
NodeKind::Auto => Some(Self::Auto(node.span())),
|
||||
@ -336,13 +347,14 @@ impl TypedNode for Lit {
|
||||
NodeKind::Angle(f, unit) => Some(Self::Angle(node.span(), *f, *unit)),
|
||||
NodeKind::Percentage(f) => Some(Self::Percent(node.span(), *f)),
|
||||
NodeKind::Fraction(f) => Some(Self::Fractional(node.span(), *f)),
|
||||
NodeKind::Str(s) => Some(Self::Str(node.span(), s.string.clone())),
|
||||
NodeKind::Str(s) => Some(Self::Str(node.span(), s.clone())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Lit {
|
||||
/// The source code location.
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::None(span) => *span,
|
||||
@ -366,7 +378,7 @@ node! {
|
||||
|
||||
impl ArrayExpr {
|
||||
/// The array items.
|
||||
pub fn items<'a>(&'a self) -> impl Iterator<Item = Expr> + 'a {
|
||||
pub fn items(&self) -> impl Iterator<Item = Expr> + '_ {
|
||||
self.0.children().filter_map(RedRef::cast)
|
||||
}
|
||||
}
|
||||
@ -378,7 +390,7 @@ node! {
|
||||
|
||||
impl DictExpr {
|
||||
/// The named dictionary items.
|
||||
pub fn items<'a>(&'a self) -> impl Iterator<Item = Named> + 'a {
|
||||
pub fn items(&self) -> impl Iterator<Item = Named> + '_ {
|
||||
self.0.children().filter_map(RedRef::cast)
|
||||
}
|
||||
}
|
||||
@ -439,7 +451,7 @@ node! {
|
||||
|
||||
impl BlockExpr {
|
||||
/// The list of expressions contained in the block.
|
||||
pub fn exprs<'a>(&'a self) -> impl Iterator<Item = Expr> + 'a {
|
||||
pub fn exprs(&self) -> impl Iterator<Item = Expr> + '_ {
|
||||
self.0.children().filter_map(RedRef::cast)
|
||||
}
|
||||
}
|
||||
@ -477,7 +489,7 @@ pub enum UnOp {
|
||||
}
|
||||
|
||||
impl TypedNode for UnOp {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
Self::from_token(node.kind())
|
||||
}
|
||||
}
|
||||
@ -581,7 +593,7 @@ pub enum BinOp {
|
||||
}
|
||||
|
||||
impl TypedNode for BinOp {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
Self::from_token(node.kind())
|
||||
}
|
||||
}
|
||||
@ -709,7 +721,7 @@ node! {
|
||||
|
||||
impl CallArgs {
|
||||
/// The positional and named arguments.
|
||||
pub fn items<'a>(&'a self) -> impl Iterator<Item = CallArg> + 'a {
|
||||
pub fn items(&self) -> impl Iterator<Item = CallArg> + '_ {
|
||||
self.0.children().filter_map(RedRef::cast)
|
||||
}
|
||||
}
|
||||
@ -726,7 +738,7 @@ pub enum CallArg {
|
||||
}
|
||||
|
||||
impl TypedNode for CallArg {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Named => Some(CallArg::Named(
|
||||
node.cast().expect("named call argument is missing name"),
|
||||
@ -767,7 +779,7 @@ impl ClosureExpr {
|
||||
}
|
||||
|
||||
/// The parameter bindings.
|
||||
pub fn params<'a>(&'a self) -> impl Iterator<Item = ClosureParam> + 'a {
|
||||
pub fn params(&self) -> impl Iterator<Item = ClosureParam> + '_ {
|
||||
self.0
|
||||
.children()
|
||||
.find(|x| x.kind() == &NodeKind::ClosureParams)
|
||||
@ -805,10 +817,10 @@ pub enum ClosureParam {
|
||||
}
|
||||
|
||||
impl TypedNode for ClosureParam {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Ident(i) => {
|
||||
Some(ClosureParam::Pos(Ident::new(i, node.span()).unwrap()))
|
||||
NodeKind::Ident(id) => {
|
||||
Some(ClosureParam::Pos(Ident::new_unchecked(id, node.span())))
|
||||
}
|
||||
NodeKind::Named => Some(ClosureParam::Named(
|
||||
node.cast().expect("named closure parameter is missing name"),
|
||||
@ -921,7 +933,7 @@ pub enum Imports {
|
||||
}
|
||||
|
||||
impl TypedNode for Imports {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Star => Some(Imports::Wildcard),
|
||||
NodeKind::ImportItems => {
|
||||
@ -1043,14 +1055,75 @@ node! {
|
||||
}
|
||||
|
||||
impl ForPattern {
|
||||
/// The key part of the pattern: index for arrays, name for dictionaries.
|
||||
pub fn key(&self) -> Option<Ident> {
|
||||
let mut items: Vec<_> = self.0.children().filter_map(RedRef::cast).collect();
|
||||
if items.len() > 1 { Some(items.remove(0)) } else { None }
|
||||
let mut children = self.0.children().filter_map(RedRef::cast);
|
||||
let key = children.next();
|
||||
if children.next().is_some() { key } else { None }
|
||||
}
|
||||
|
||||
/// The value part of the pattern.
|
||||
pub fn value(&self) -> Ident {
|
||||
self.0
|
||||
.cast_last_child()
|
||||
.expect("for-in loop pattern is missing value")
|
||||
}
|
||||
}
|
||||
|
||||
/// An unicode identifier with a few extra permissible characters.
|
||||
///
|
||||
/// In addition to what is specified in the [Unicode Standard][uax31], we allow:
|
||||
/// - `_` as a starting character,
|
||||
/// - `_` and `-` as continuing characters.
|
||||
///
|
||||
/// [uax31]: http://www.unicode.org/reports/tr31/
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Ident {
|
||||
/// The source code location.
|
||||
pub span: Span,
|
||||
/// The identifier string.
|
||||
pub string: EcoString,
|
||||
}
|
||||
|
||||
impl Ident {
|
||||
/// Create a new identifier from a string checking that it is a valid.
|
||||
pub fn new(
|
||||
string: impl AsRef<str> + Into<EcoString>,
|
||||
span: impl Into<Span>,
|
||||
) -> Option<Self> {
|
||||
is_ident(string.as_ref())
|
||||
.then(|| Self { span: span.into(), string: string.into() })
|
||||
}
|
||||
|
||||
/// Create a new identifier from a string and a span.
|
||||
///
|
||||
/// The `string` must be a valid identifier.
|
||||
#[track_caller]
|
||||
pub fn new_unchecked(string: impl Into<EcoString>, span: Span) -> Self {
|
||||
let string = string.into();
|
||||
debug_assert!(is_ident(&string), "`{}` is not a valid identifier", string);
|
||||
Self { span, string }
|
||||
}
|
||||
|
||||
/// Return a reference to the underlying string.
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.string
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Ident {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedNode for Ident {
|
||||
fn from_red(node: RedRef) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Ident(string) => Some(Ident::new_unchecked(string, node.span())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,94 +0,0 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::ops::Deref;
|
||||
|
||||
use unicode_xid::UnicodeXID;
|
||||
|
||||
use super::{NodeKind, RedRef, Span, TypedNode};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// An unicode identifier with a few extra permissible characters.
|
||||
///
|
||||
/// In addition to what is specified in the [Unicode Standard][uax31], we allow:
|
||||
/// - `_` as a starting character,
|
||||
/// - `_` and `-` as continuing characters.
|
||||
///
|
||||
/// [uax31]: http://www.unicode.org/reports/tr31/
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Ident {
|
||||
/// The source code location.
|
||||
pub span: Span,
|
||||
/// The identifier string.
|
||||
pub string: EcoString,
|
||||
}
|
||||
|
||||
impl Ident {
|
||||
/// Create a new identifier from a string checking that it is a valid.
|
||||
pub fn new(
|
||||
string: impl AsRef<str> + Into<EcoString>,
|
||||
span: impl Into<Span>,
|
||||
) -> Option<Self> {
|
||||
if is_ident(string.as_ref()) {
|
||||
Some(Self { span: span.into(), string: string.into() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a reference to the underlying string.
|
||||
pub fn as_str(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Ident {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.string.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Ident {
|
||||
fn as_ref(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for Ident {
|
||||
fn borrow(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Ident> for EcoString {
|
||||
fn from(ident: &Ident) -> Self {
|
||||
ident.string.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedNode for Ident {
|
||||
fn cast_from(node: RedRef) -> Option<Self> {
|
||||
match node.kind() {
|
||||
NodeKind::Ident(i) => Some(Ident::new(i, node.span()).unwrap()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a string is a valid identifier.
|
||||
pub fn is_ident(string: &str) -> bool {
|
||||
let mut chars = string.chars();
|
||||
chars
|
||||
.next()
|
||||
.map_or(false, |c| is_id_start(c) && chars.all(is_id_continue))
|
||||
}
|
||||
|
||||
/// Whether a character can start an identifier.
|
||||
pub fn is_id_start(c: char) -> bool {
|
||||
c.is_xid_start() || c == '_'
|
||||
}
|
||||
|
||||
/// Whether a character can continue an identifier.
|
||||
pub fn is_id_continue(c: char) -> bool {
|
||||
c.is_xid_continue() || c == '_' || c == '-'
|
||||
}
|
@ -1,31 +1,28 @@
|
||||
//! Syntax types.
|
||||
|
||||
mod ast;
|
||||
mod ident;
|
||||
pub mod ast;
|
||||
mod pretty;
|
||||
mod span;
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub use ast::*;
|
||||
pub use ident::*;
|
||||
pub use pretty::*;
|
||||
pub use span::*;
|
||||
|
||||
use self::ast::TypedNode;
|
||||
use crate::diag::Error;
|
||||
use crate::geom::{AngularUnit, LengthUnit};
|
||||
use crate::source::SourceId;
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// Children of a [`GreenNode`].
|
||||
/// An inner of leaf node in the untyped green tree.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Green {
|
||||
/// A non-terminal node in an Rc.
|
||||
/// A reference-counted inner node.
|
||||
Node(Rc<GreenNode>),
|
||||
/// A terminal owned token.
|
||||
/// A terminal, owned token.
|
||||
Token(GreenData),
|
||||
}
|
||||
|
||||
@ -77,13 +74,12 @@ impl Debug for Green {
|
||||
f.debug_list().entries(&n.children).finish()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A syntactical node.
|
||||
#[derive(Clone, PartialEq)]
|
||||
/// An inner node in the untyped green tree.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct GreenNode {
|
||||
/// Node metadata.
|
||||
data: GreenData,
|
||||
@ -122,15 +118,15 @@ impl From<Rc<GreenNode>> for Green {
|
||||
}
|
||||
}
|
||||
|
||||
/// Data shared between [`GreenNode`]s and leaf nodes.
|
||||
#[derive(Clone, PartialEq)]
|
||||
/// Data shared between inner and leaf nodes.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct GreenData {
|
||||
/// What kind of node this is (each kind would have its own struct in a
|
||||
/// strongly typed AST).
|
||||
kind: NodeKind,
|
||||
/// The byte length of the node in the source.
|
||||
len: usize,
|
||||
/// Whether this node or any of its children are erroneous.
|
||||
/// Whether this node or any of its children contain an error.
|
||||
erroneous: bool,
|
||||
}
|
||||
|
||||
@ -162,8 +158,9 @@ impl From<GreenData> for Green {
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed wrapper for the [`GreenNode`] type that allows to access spans,
|
||||
/// error lists and cast to an AST.
|
||||
/// A borrowed wrapper for a [`GreenNode`] with span information.
|
||||
///
|
||||
/// Borrowed variant of [`RedNode`]. Can be [cast](Self::cast) to an AST node.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct RedRef<'a> {
|
||||
id: SourceId,
|
||||
@ -182,50 +179,27 @@ impl<'a> RedRef<'a> {
|
||||
}
|
||||
|
||||
/// The type of the node.
|
||||
pub fn kind(&self) -> &NodeKind {
|
||||
pub fn kind(self) -> &'a NodeKind {
|
||||
self.green.kind()
|
||||
}
|
||||
|
||||
/// The span of the node.
|
||||
pub fn span(&self) -> Span {
|
||||
Span::new(self.id, self.offset, self.offset + self.green.len())
|
||||
}
|
||||
|
||||
/// The length of the node.
|
||||
pub fn len(&self) -> usize {
|
||||
pub fn len(self) -> usize {
|
||||
self.green.len()
|
||||
}
|
||||
|
||||
/// Convert the node to a typed AST node.
|
||||
pub fn cast<T>(self) -> Option<T>
|
||||
where
|
||||
T: TypedNode,
|
||||
{
|
||||
T::cast_from(self)
|
||||
/// The span of the node.
|
||||
pub fn span(self) -> Span {
|
||||
Span::new(self.id, self.offset, self.offset + self.green.len())
|
||||
}
|
||||
|
||||
/// Whether the node or its children contain an error.
|
||||
pub fn erroneous(&self) -> bool {
|
||||
pub fn erroneous(self) -> bool {
|
||||
self.green.erroneous()
|
||||
}
|
||||
|
||||
/// The node's children.
|
||||
pub fn children(self) -> impl Iterator<Item = RedRef<'a>> + Clone {
|
||||
let children = match &self.green {
|
||||
Green::Node(node) => node.children(),
|
||||
Green::Token(_) => &[],
|
||||
};
|
||||
|
||||
let mut offset = self.offset;
|
||||
children.iter().map(move |green| {
|
||||
let child_offset = offset;
|
||||
offset += green.len();
|
||||
RedRef { id: self.id, offset: child_offset, green }
|
||||
})
|
||||
}
|
||||
|
||||
/// The error messages for this node and its descendants.
|
||||
pub fn errors(&self) -> Vec<Error> {
|
||||
pub fn errors(self) -> Vec<Error> {
|
||||
if !self.green.erroneous() {
|
||||
return vec![];
|
||||
}
|
||||
@ -248,19 +222,42 @@ impl<'a> RedRef<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the node to a typed AST node.
|
||||
pub fn cast<T>(self) -> Option<T>
|
||||
where
|
||||
T: TypedNode,
|
||||
{
|
||||
T::from_red(self)
|
||||
}
|
||||
|
||||
/// The node's children.
|
||||
pub fn children(self) -> impl Iterator<Item = RedRef<'a>> {
|
||||
let children = match &self.green {
|
||||
Green::Node(node) => node.children(),
|
||||
Green::Token(_) => &[],
|
||||
};
|
||||
|
||||
let mut offset = self.offset;
|
||||
children.iter().map(move |green| {
|
||||
let child_offset = offset;
|
||||
offset += green.len();
|
||||
RedRef { id: self.id, offset: child_offset, green }
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the first child of some type.
|
||||
pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option<RedRef> {
|
||||
pub(crate) fn typed_child(self, kind: &NodeKind) -> Option<RedRef<'a>> {
|
||||
self.children()
|
||||
.find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind))
|
||||
}
|
||||
|
||||
/// Get the first child that can cast to some AST type.
|
||||
pub(crate) fn cast_first_child<T: TypedNode>(&self) -> Option<T> {
|
||||
pub(crate) fn cast_first_child<T: TypedNode>(self) -> Option<T> {
|
||||
self.children().find_map(RedRef::cast)
|
||||
}
|
||||
|
||||
/// Get the last child that can cast to some AST type.
|
||||
pub(crate) fn cast_last_child<T: TypedNode>(&self) -> Option<T> {
|
||||
pub(crate) fn cast_last_child<T: TypedNode>(self) -> Option<T> {
|
||||
self.children().filter_map(RedRef::cast).last()
|
||||
}
|
||||
}
|
||||
@ -277,8 +274,9 @@ impl Debug for RedRef<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An owned wrapper for the [`GreenNode`] type that allows to access spans,
|
||||
/// error lists and cast to an AST.
|
||||
/// A owned wrapper for a [`GreenNode`] with span information.
|
||||
///
|
||||
/// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST nodes.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct RedNode {
|
||||
id: SourceId,
|
||||
@ -293,7 +291,7 @@ impl RedNode {
|
||||
}
|
||||
|
||||
/// Convert to a borrowed representation.
|
||||
pub fn as_ref<'a>(&'a self) -> RedRef<'a> {
|
||||
pub fn as_ref(&self) -> RedRef<'_> {
|
||||
RedRef {
|
||||
id: self.id,
|
||||
offset: self.offset,
|
||||
@ -301,9 +299,9 @@ impl RedNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// The span of the node.
|
||||
pub fn span(&self) -> Span {
|
||||
self.as_ref().span()
|
||||
/// The type of the node.
|
||||
pub fn kind(&self) -> &NodeKind {
|
||||
self.as_ref().kind()
|
||||
}
|
||||
|
||||
/// The length of the node.
|
||||
@ -311,29 +309,29 @@ impl RedNode {
|
||||
self.as_ref().len()
|
||||
}
|
||||
|
||||
/// The span of the node.
|
||||
pub fn span(&self) -> Span {
|
||||
self.as_ref().span()
|
||||
}
|
||||
|
||||
/// The error messages for this node and its descendants.
|
||||
pub fn errors(&self) -> Vec<Error> {
|
||||
self.as_ref().errors()
|
||||
}
|
||||
|
||||
/// Convert the node to a typed AST node.
|
||||
pub fn cast<T>(self) -> Option<T>
|
||||
where
|
||||
T: TypedNode,
|
||||
{
|
||||
T::cast_from(self.as_ref())
|
||||
}
|
||||
|
||||
/// The type of the node.
|
||||
pub fn kind(&self) -> &NodeKind {
|
||||
self.green.kind()
|
||||
self.as_ref().cast()
|
||||
}
|
||||
|
||||
/// The children of the node.
|
||||
pub fn children<'a>(&'a self) -> impl Iterator<Item = RedRef<'a>> + Clone {
|
||||
pub fn children(&self) -> impl Iterator<Item = RedRef<'_>> {
|
||||
self.as_ref().children()
|
||||
}
|
||||
|
||||
/// The error messages for this node and its descendants.
|
||||
pub fn errors<'a>(&'a self) -> Vec<Error> {
|
||||
self.as_ref().errors()
|
||||
}
|
||||
|
||||
/// Get the first child of some type.
|
||||
pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option<RedNode> {
|
||||
self.as_ref().typed_child(kind).map(RedRef::own)
|
||||
@ -356,11 +354,10 @@ impl Debug for RedNode {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TypedNode: Sized {
|
||||
/// Performs the conversion.
|
||||
fn cast_from(value: RedRef) -> Option<Self>;
|
||||
}
|
||||
|
||||
/// All syntactical building blocks that can be part of a Typst document.
|
||||
///
|
||||
/// Can be emitted as a token by the tokenizer or as part of a green node by
|
||||
/// the parser.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum NodeKind {
|
||||
/// A left square bracket: `[`.
|
||||
@ -469,7 +466,7 @@ pub enum NodeKind {
|
||||
EmDash,
|
||||
/// A slash and the letter "u" followed by a hexadecimal unicode entity
|
||||
/// enclosed in curly braces: `\u{1F5FA}`.
|
||||
UnicodeEscape(UnicodeEscapeData),
|
||||
UnicodeEscape(char),
|
||||
/// Strong text was enabled / disabled: `*`.
|
||||
Strong,
|
||||
/// Emphasized text was enabled / disabled: `_`.
|
||||
@ -508,12 +505,12 @@ pub enum NodeKind {
|
||||
/// A percentage: `50%`.
|
||||
///
|
||||
/// _Note_: `50%` is stored as `50.0` here, as in the corresponding
|
||||
/// [literal](Lit::Percent).
|
||||
/// [literal](ast::Lit::Percent).
|
||||
Percentage(f64),
|
||||
/// A fraction unit: `3fr`.
|
||||
Fraction(f64),
|
||||
/// A quoted string: `"..."`.
|
||||
Str(StrData),
|
||||
Str(EcoString),
|
||||
/// An array expression: `(1, "hi", 12cm)`.
|
||||
Array,
|
||||
/// A dictionary expression: `(thickness: 3pt, pattern: dashed)`.
|
||||
@ -572,24 +569,7 @@ pub enum NodeKind {
|
||||
Unknown(EcoString),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ErrorPosition {
|
||||
/// At the start of the node.
|
||||
Start,
|
||||
/// Over the full width of the node.
|
||||
Full,
|
||||
/// At the end of the node.
|
||||
End,
|
||||
}
|
||||
|
||||
/// A quoted string token: `"..."`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct StrData {
|
||||
/// The string inside the quotes.
|
||||
pub string: EcoString,
|
||||
}
|
||||
|
||||
/// A raw block token: `` `...` ``.
|
||||
/// Payload of a raw block: `` `...` ``.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RawData {
|
||||
/// The raw text in the block.
|
||||
@ -602,7 +582,7 @@ pub struct RawData {
|
||||
pub block: bool,
|
||||
}
|
||||
|
||||
/// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`.
|
||||
/// Payload of a math formula: `$2pi + x$` or `$[f'(x) = x^2]$`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MathData {
|
||||
/// The formula between the dollars.
|
||||
@ -612,17 +592,15 @@ pub struct MathData {
|
||||
pub display: bool,
|
||||
}
|
||||
|
||||
/// A unicode escape sequence token: `\u{1F5FA}`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct UnicodeEscapeData {
|
||||
/// The resulting unicode character.
|
||||
pub character: char,
|
||||
}
|
||||
|
||||
impl Display for NodeKind {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.pad(self.as_str())
|
||||
}
|
||||
/// Where in a node an error should be annotated.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ErrorPosition {
|
||||
/// At the start of the node.
|
||||
Start,
|
||||
/// Over the full width of the node.
|
||||
Full,
|
||||
/// At the end of the node.
|
||||
End,
|
||||
}
|
||||
|
||||
impl NodeKind {
|
||||
@ -658,6 +636,7 @@ impl NodeKind {
|
||||
matches!(self, NodeKind::Error(_, _) | NodeKind::Unknown(_))
|
||||
}
|
||||
|
||||
/// A human-readable name for the kind.
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::LeftBracket => "opening bracket",
|
||||
@ -764,3 +743,9 @@ impl NodeKind {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NodeKind {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.pad(self.as_str())
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use std::fmt::{self, Arguments, Write};
|
||||
|
||||
use super::*;
|
||||
use super::ast::*;
|
||||
|
||||
/// Pretty print an item and return the resulting string.
|
||||
pub fn pretty<T>(item: &T) -> String
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::ops::{Add, Range};
|
||||
use std::ops::Range;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -53,23 +53,19 @@ pub struct Span {
|
||||
/// The id of the source file.
|
||||
pub source: SourceId,
|
||||
/// The inclusive start position.
|
||||
pub start: Pos,
|
||||
pub start: usize,
|
||||
/// The inclusive end position.
|
||||
pub end: Pos,
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// Create a new span from start and end positions.
|
||||
pub fn new(source: SourceId, start: impl Into<Pos>, end: impl Into<Pos>) -> Self {
|
||||
Self {
|
||||
source,
|
||||
start: start.into(),
|
||||
end: end.into(),
|
||||
}
|
||||
pub fn new(source: SourceId, start: usize, end: usize) -> Self {
|
||||
Self { source, start, end }
|
||||
}
|
||||
|
||||
/// Create a span including just a single position.
|
||||
pub fn at(source: SourceId, pos: impl Into<Pos> + Copy) -> Self {
|
||||
pub fn at(source: SourceId, pos: usize) -> Self {
|
||||
Self::new(source, pos, pos)
|
||||
}
|
||||
|
||||
@ -77,19 +73,29 @@ impl Span {
|
||||
pub fn detached() -> Self {
|
||||
Self {
|
||||
source: SourceId::from_raw(0),
|
||||
start: Pos::ZERO,
|
||||
end: Pos::ZERO,
|
||||
start: 0,
|
||||
end: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a span with a different start position.
|
||||
pub fn with_start(self, start: impl Into<Pos>) -> Self {
|
||||
Self { start: start.into(), ..self }
|
||||
pub fn with_start(self, start: usize) -> Self {
|
||||
Self { start, ..self }
|
||||
}
|
||||
|
||||
/// Create a span with a different end position.
|
||||
pub fn with_end(self, end: impl Into<Pos>) -> Self {
|
||||
Self { end: end.into(), ..self }
|
||||
pub fn with_end(self, end: usize) -> Self {
|
||||
Self { end, ..self }
|
||||
}
|
||||
|
||||
/// A new span at the position of this span's start.
|
||||
pub fn at_start(&self) -> Span {
|
||||
Self::at(self.source, self.start)
|
||||
}
|
||||
|
||||
/// A new span at the position of this span's end.
|
||||
pub fn at_end(&self) -> Span {
|
||||
Self::at(self.source, self.end)
|
||||
}
|
||||
|
||||
/// Create a new span with the earlier start and later end position.
|
||||
@ -110,28 +116,18 @@ impl Span {
|
||||
}
|
||||
|
||||
/// Test whether a position is within the span.
|
||||
pub fn contains_pos(&self, pos: Pos) -> bool {
|
||||
pub fn contains(&self, pos: usize) -> bool {
|
||||
self.start <= pos && self.end >= pos
|
||||
}
|
||||
|
||||
/// Test whether one span complete contains the other span.
|
||||
pub fn contains(self, other: Self) -> bool {
|
||||
pub fn surrounds(self, other: Self) -> bool {
|
||||
self.source == other.source && self.start <= other.start && self.end >= other.end
|
||||
}
|
||||
|
||||
/// Convert to a `Range<Pos>` for indexing.
|
||||
/// Convert to a `Range<usize>` for indexing.
|
||||
pub fn to_range(self) -> Range<usize> {
|
||||
self.start.to_usize() .. self.end.to_usize()
|
||||
}
|
||||
|
||||
/// A new span at the position of this span's start.
|
||||
pub fn at_start(&self) -> Span {
|
||||
Self::at(self.source, self.start)
|
||||
}
|
||||
|
||||
/// A new span at the position of this span's end.
|
||||
pub fn at_end(&self) -> Span {
|
||||
Self::at(self.source, self.end)
|
||||
self.start .. self.end
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,77 +146,3 @@ impl PartialOrd for Span {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A byte position in source code.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Pos(pub u32);
|
||||
|
||||
impl Pos {
|
||||
/// The zero position.
|
||||
pub const ZERO: Self = Self(0);
|
||||
|
||||
/// Convert to a usize for indexing.
|
||||
pub fn to_usize(self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Pos {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Pos {
|
||||
fn from(index: u32) -> Self {
|
||||
Self(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Pos {
|
||||
fn from(index: usize) -> Self {
|
||||
Self(index as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Add<T> for Pos
|
||||
where
|
||||
T: Into<Pos>,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: T) -> Self {
|
||||
Pos(self.0 + rhs.into().0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a position or range into a span.
|
||||
pub trait IntoSpan {
|
||||
/// Convert into a span by providing the source id.
|
||||
fn into_span(self, source: SourceId) -> Span;
|
||||
}
|
||||
|
||||
impl IntoSpan for Span {
|
||||
fn into_span(self, source: SourceId) -> Span {
|
||||
debug_assert_eq!(self.source, source);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoSpan for Pos {
|
||||
fn into_span(self, source: SourceId) -> Span {
|
||||
Span::new(source, self, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoSpan for usize {
|
||||
fn into_span(self, source: SourceId) -> Span {
|
||||
Span::new(source, self, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoSpan for Range<usize> {
|
||||
fn into_span(self, source: SourceId) -> Span {
|
||||
Span::new(source, self.start, self.end)
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ use typst::loading::FsLoader;
|
||||
use typst::parse::Scanner;
|
||||
use typst::source::SourceFile;
|
||||
use typst::style::Style;
|
||||
use typst::syntax::{Pos, Span};
|
||||
use typst::syntax::Span;
|
||||
use typst::Context;
|
||||
|
||||
const TYP_DIR: &str = "./typ";
|
||||
@ -355,12 +355,12 @@ fn parse_metadata(source: &SourceFile) -> (Option<bool>, Vec<Error>) {
|
||||
let comments =
|
||||
lines[i ..].iter().take_while(|line| line.starts_with("//")).count();
|
||||
|
||||
let pos = |s: &mut Scanner| -> Pos {
|
||||
let pos = |s: &mut Scanner| -> usize {
|
||||
let first = num(s) - 1;
|
||||
let (delta, column) =
|
||||
if s.eat_if(':') { (first, num(s) - 1) } else { (0, first) };
|
||||
let line = (i + comments) + delta;
|
||||
source.line_column_to_byte(line, column).unwrap().into()
|
||||
source.line_column_to_byte(line, column).unwrap()
|
||||
};
|
||||
|
||||
let mut s = Scanner::new(rest);
|
||||
@ -375,10 +375,10 @@ fn parse_metadata(source: &SourceFile) -> (Option<bool>, Vec<Error>) {
|
||||
}
|
||||
|
||||
fn print_error(source: &SourceFile, line: usize, error: &Error) {
|
||||
let start_line = 1 + line + source.byte_to_line(error.span.start.to_usize()).unwrap();
|
||||
let start_col = 1 + source.byte_to_column(error.span.start.to_usize()).unwrap();
|
||||
let end_line = 1 + line + source.byte_to_line(error.span.end.to_usize()).unwrap();
|
||||
let end_col = 1 + source.byte_to_column(error.span.end.to_usize()).unwrap();
|
||||
let start_line = 1 + line + source.byte_to_line(error.span.start).unwrap();
|
||||
let start_col = 1 + source.byte_to_column(error.span.start).unwrap();
|
||||
let end_line = 1 + line + source.byte_to_line(error.span.end).unwrap();
|
||||
let end_col = 1 + source.byte_to_column(error.span.end).unwrap();
|
||||
println!(
|
||||
"Error: {}:{}-{}:{}: {}",
|
||||
start_line, start_col, end_line, end_col, error.message
|
||||
|
Loading…
x
Reference in New Issue
Block a user