summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-10-17 19:26:24 +0200
committerLaurenz <laurmaedje@gmail.com>2022-10-17 20:04:22 +0200
commite21822665591dc19766275da1e185215a6b945ef (patch)
tree7788e211c3c33c8b5a8ad7d5eb7574e33631eb16
parent4fd031a256b2ecfe524859d5599fafb386395572 (diff)
Merge some modules
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml61
-rw-r--r--benches/oneshot.rs13
-rw-r--r--macros/src/lib.rs4
-rw-r--r--src/frame.rs2
-rw-r--r--src/lib.rs19
-rw-r--r--src/library/prelude.rs6
-rw-r--r--src/library/text/lang.rs2
-rw-r--r--src/library/text/quotes.rs2
-rw-r--r--src/library/text/raw.rs4
-rw-r--r--src/library/utility/mod.rs4
-rw-r--r--src/library/utility/string.rs2
-rw-r--r--src/main.rs2
-rw-r--r--src/model/args.rs (renamed from src/eval/args.rs)0
-rw-r--r--src/model/array.rs (renamed from src/eval/array.rs)4
-rw-r--r--src/model/capture.rs (renamed from src/eval/capture.rs)2
-rw-r--r--src/model/cast.rs (renamed from src/eval/cast.rs)27
-rw-r--r--src/model/content.rs31
-rw-r--r--src/model/dict.rs (renamed from src/eval/dict.rs)4
-rw-r--r--src/model/eval.rs (renamed from src/eval/mod.rs)40
-rw-r--r--src/model/func.rs (renamed from src/eval/func.rs)7
-rw-r--r--src/model/layout.rs17
-rw-r--r--src/model/methods.rs (renamed from src/eval/methods.rs)0
-rw-r--r--src/model/mod.rs34
-rw-r--r--src/model/ops.rs (renamed from src/eval/ops.rs)9
-rw-r--r--src/model/property.rs3
-rw-r--r--src/model/raw.rs (renamed from src/eval/raw.rs)3
-rw-r--r--src/model/recipe.rs6
-rw-r--r--src/model/scope.rs (renamed from src/eval/scope.rs)0
-rw-r--r--src/model/show.rs3
-rw-r--r--src/model/str.rs (renamed from src/eval/str.rs)85
-rw-r--r--src/model/styles.rs2
-rw-r--r--src/model/value.rs (renamed from src/eval/value.rs)15
-rw-r--r--src/model/vm.rs (renamed from src/eval/vm.rs)3
-rw-r--r--src/syntax/highlight.rs6
-rw-r--r--src/syntax/incremental.rs (renamed from src/parse/incremental.rs)10
-rw-r--r--src/syntax/mod.rs567
-rw-r--r--src/syntax/node.rs548
-rw-r--r--src/syntax/parser.rs (renamed from src/parse/parser.rs)3
-rw-r--r--src/syntax/parsing.rs (renamed from src/parse/mod.rs)42
-rw-r--r--src/syntax/resolve.rs (renamed from src/parse/resolve.rs)3
-rw-r--r--src/syntax/source.rs (renamed from src/source.rs)2
-rw-r--r--src/syntax/span.rs6
-rw-r--r--src/syntax/tokens.rs (renamed from src/parse/tokens.rs)4
-rw-r--r--tests/typeset.rs6
45 files changed, 801 insertions, 822 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e8a65066..e1eabd01 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -375,15 +375,6 @@ dependencies = [
]
[[package]]
-name = "fxhash"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
-dependencies = [
- "byteorder",
-]
-
-[[package]]
name = "getrandom"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1126,7 +1117,6 @@ dependencies = [
"dirs",
"elsa",
"flate2",
- "fxhash",
"hypher",
"iai",
"image",
diff --git a/Cargo.toml b/Cargo.toml
index 1c96229b..402419de 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,25 +11,23 @@ typst-macros = { path = "./macros" }
# Utilities
bitflags = "1"
bytemuck = "1"
-fxhash = "0.2"
+comemo = "0.1"
once_cell = "1"
+regex = "1"
serde = { version = "1", features = ["derive"] }
+siphasher = "0.3"
typed-arena = "2"
unscanny = "0.1"
-regex = "1"
-
-# Incremental compilation
-comemo = "0.1"
# Text and font handling
hypher = "0.1"
kurbo = "0.8"
-ttf-parser = "0.17"
rustybuzz = "0.5"
+ttf-parser = "0.17"
unicode-bidi = "0.3.5"
+unicode-script = "0.5"
unicode-segmentation = "1"
unicode-xid = "0.2"
-unicode-script = "0.5"
xi-unicode = "0.3"
# Raster and vector graphics handling
@@ -37,12 +35,12 @@ image = { version = "0.24", default-features = false, features = ["png", "jpeg",
usvg = { version = "0.22", default-features = false }
# External implementation of user-facing features
-syntect = { version = "5", default-features = false, features = ["default-syntaxes", "regex-fancy"] }
-rex = { git = "https://github.com/laurmaedje/ReX" }
-unicode-math = { git = "https://github.com/s3bk/unicode-math/" }
-lipsum = { git = "https://github.com/reknih/lipsum" }
csv = "1"
+lipsum = { git = "https://github.com/reknih/lipsum" }
+rex = { git = "https://github.com/laurmaedje/ReX" }
serde_json = "1"
+syntect = { version = "5", default-features = false, features = ["default-syntaxes", "regex-fancy"] }
+unicode-math = { git = "https://github.com/s3bk/unicode-math/" }
# PDF export
miniz_oxide = "0.5"
@@ -50,52 +48,50 @@ pdf-writer = "0.6"
subsetter = "0.1"
svg2pdf = "0.4"
-# Raster export / rendering
-tiny-skia = "0.6.2"
+# Rendering
+flate2 = "1"
pixglyph = { git = "https://github.com/typst/pixglyph" }
resvg = { version = "0.22", default-features = false }
roxmltree = "0.14"
-flate2 = "1"
+tiny-skia = "0.6.2"
-# Command line interface / tests
-pico-args = { version = "0.4", optional = true }
+# Command line interface
+chrono = { version = "0.4", default-features = false, features = ["clock", "std"], optional = true }
codespan-reporting = { version = "0.11", optional = true }
-same-file = { version = "1", optional = true }
-walkdir = { version = "2", optional = true }
-elsa = { version = "1.7", optional = true }
dirs = { version = "4", optional = true }
+elsa = { version = "1.7", optional = true }
memmap2 = { version = "0.5", optional = true }
-siphasher = { version = "0.3", optional = true }
notify = { version = "5", optional = true }
-chrono = { version = "0.4", default-features = false, features = ["clock", "std"], optional = true }
+pico-args = { version = "0.4", optional = true }
+same-file = { version = "1", optional = true }
+walkdir = { version = "2", optional = true }
[dev-dependencies]
iai = { git = "https://github.com/reknih/iai" }
walkdir = "2"
+elsa = "1.7"
+
+[workspace]
+members = ["macros"]
[features]
-default = ["tests"]
-tests = ["same-file", "walkdir", "elsa", "siphasher"]
cli = [
- "pico-args",
+ "chrono",
"codespan-reporting",
"dirs",
+ "elsa",
"memmap2",
+ "notify",
+ "pico-args",
"same-file",
"walkdir",
- "elsa",
- "siphasher",
- "notify",
- "chrono",
]
[profile.dev]
-# Faster compilation
-debug = 0
+debug = 0 # Faster compilation
[profile.dev.package."*"]
-# Faster test execution
-opt-level = 2
+opt-level = 2 # Faster test execution
[[bin]]
name = "typst"
@@ -103,7 +99,6 @@ required-features = ["cli"]
[[test]]
name = "typeset"
-required-features = ["tests"]
harness = false
[[bench]]
diff --git a/benches/oneshot.rs b/benches/oneshot.rs
index d4727512..1d82f44d 100644
--- a/benches/oneshot.rs
+++ b/benches/oneshot.rs
@@ -6,8 +6,7 @@ use unscanny::Scanner;
use typst::diag::{FileError, FileResult};
use typst::font::{Font, FontBook};
-use typst::parse::{TokenMode, Tokens};
-use typst::source::{Source, SourceId};
+use typst::syntax::{Source, SourceId, TokenMode, Tokens};
use typst::util::Buffer;
use typst::{Config, World};
@@ -55,7 +54,7 @@ fn bench_tokenize(iai: &mut Iai) {
}
fn bench_parse(iai: &mut Iai) {
- iai.run(|| typst::parse::parse(TEXT));
+ iai.run(|| typst::syntax::parse(TEXT));
}
fn bench_edit(iai: &mut Iai) {
@@ -77,15 +76,15 @@ fn bench_highlight(iai: &mut Iai) {
fn bench_eval(iai: &mut Iai) {
let world = BenchWorld::new();
let id = world.source.id();
- let route = typst::eval::Route::default();
- iai.run(|| typst::eval::eval(world.track(), route.track(), id).unwrap());
+ let route = typst::model::Route::default();
+ iai.run(|| typst::model::eval(world.track(), route.track(), id).unwrap());
}
fn bench_layout(iai: &mut Iai) {
let world = BenchWorld::new();
let id = world.source.id();
- let route = typst::eval::Route::default();
- let module = typst::eval::eval(world.track(), route.track(), id).unwrap();
+ let route = typst::model::Route::default();
+ let module = typst::model::eval(world.track(), route.track(), id).unwrap();
iai.run(|| typst::model::layout(world.track(), &module.content));
}
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 052e7fcf..62e27a09 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -69,12 +69,12 @@ fn expand(stream: TokenStream2, mut impl_block: syn::ItemImpl) -> Result<TokenSt
use std::any::TypeId;
use std::marker::PhantomData;
use once_cell::sync::Lazy;
- use crate::{eval, model};
+ use crate::model;
use super::*;
#impl_block
- impl<#params> eval::Node for #self_ty {
+ impl<#params> model::Node for #self_ty {
const SHOWABLE: bool = #showable;
#construct
#set
diff --git a/src/frame.rs b/src/frame.rs
index 287bbf90..c7827a9c 100644
--- a/src/frame.rs
+++ b/src/frame.rs
@@ -4,13 +4,13 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::num::NonZeroUsize;
use std::sync::Arc;
-use crate::eval::{Dict, Value};
use crate::font::Font;
use crate::geom::{
Align, Em, Length, Numeric, Paint, Point, Shape, Size, Spec, Transform,
};
use crate::image::Image;
use crate::library::text::Lang;
+use crate::model::{Dict, Value};
use crate::util::EcoString;
/// A finished layout with elements at fixed positions.
diff --git a/src/lib.rs b/src/lib.rs
index e288d556..25f59aae 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -17,12 +17,12 @@
//! - **Exporting:** The finished layout can be exported into a supported
//! format. Currently, the only supported output format is [PDF].
//!
-//! [tokens]: parse::Tokens
-//! [parsed]: parse::parse
+//! [tokens]: syntax::Tokens
+//! [parsed]: syntax::parse
//! [syntax tree]: syntax::SyntaxNode
//! [AST]: syntax::ast
-//! [evaluate]: eval::eval
-//! [module]: eval::Module
+//! [evaluate]: model::eval
+//! [module]: model::Module
//! [content]: model::Content
//! [layouted]: model::layout
//! [PDF]: export::pdf
@@ -38,15 +38,12 @@ pub mod geom;
#[macro_use]
pub mod diag;
#[macro_use]
-pub mod eval;
+pub mod model;
pub mod export;
pub mod font;
pub mod frame;
pub mod image;
pub mod library;
-pub mod model;
-pub mod parse;
-pub mod source;
pub mod syntax;
use std::path::{Path, PathBuf};
@@ -54,11 +51,11 @@ use std::path::{Path, PathBuf};
use comemo::{Prehashed, Track};
use crate::diag::{FileResult, SourceResult};
-use crate::eval::{Route, Scope};
use crate::font::{Font, FontBook};
use crate::frame::Frame;
use crate::model::StyleMap;
-use crate::source::{Source, SourceId};
+use crate::model::{Route, Scope};
+use crate::syntax::{Source, SourceId};
use crate::util::Buffer;
/// Typeset a source file into a collection of layouted frames.
@@ -71,7 +68,7 @@ pub fn typeset(
main: SourceId,
) -> SourceResult<Vec<Frame>> {
let route = Route::default();
- let module = eval::eval(world.track(), route.track(), main)?;
+ let module = model::eval(world.track(), route.track(), main)?;
model::layout(world.track(), &module.content)
}
diff --git a/src/library/prelude.rs b/src/library/prelude.rs
index 03bef51e..7a4284f1 100644
--- a/src/library/prelude.rs
+++ b/src/library/prelude.rs
@@ -12,12 +12,12 @@ pub use typst_macros::node;
pub use crate::diag::{
with_alternative, At, FileError, FileResult, SourceError, SourceResult, StrResult,
};
-pub use crate::eval::{
+pub use crate::frame::*;
+pub use crate::geom::*;
+pub use crate::model::{
Arg, Args, Array, Cast, Dict, Dynamic, Func, Node, RawAlign, RawLength, RawStroke,
Scope, Smart, Str, Value, Vm,
};
-pub use crate::frame::*;
-pub use crate::geom::*;
pub use crate::model::{
Content, Fold, Key, Layout, LayoutNode, Regions, Resolve, Selector, Show, ShowNode,
StyleChain, StyleMap, StyleVec,
diff --git a/src/library/text/lang.rs b/src/library/text/lang.rs
index b75b3cd8..f2193f05 100644
--- a/src/library/text/lang.rs
+++ b/src/library/text/lang.rs
@@ -1,5 +1,5 @@
-use crate::eval::Value;
use crate::geom::Dir;
+use crate::model::Value;
/// A code for a natural language.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
diff --git a/src/library/text/quotes.rs b/src/library/text/quotes.rs
index 98402ca4..0a22646a 100644
--- a/src/library/text/quotes.rs
+++ b/src/library/text/quotes.rs
@@ -1,5 +1,5 @@
use super::{Lang, Region};
-use crate::parse::is_newline;
+use crate::syntax::is_newline;
/// State machine for smart quote subtitution.
#[derive(Debug, Clone)]
diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs
index 8b0874f8..35160073 100644
--- a/src/library/text/raw.rs
+++ b/src/library/text/raw.rs
@@ -72,8 +72,8 @@ impl Show for RawNode {
let mut realized = if matches!(lang.as_deref(), Some("typ" | "typst" | "typc")) {
let root = match lang.as_deref() {
- Some("typc") => crate::parse::parse_code(&self.text),
- _ => crate::parse::parse(&self.text),
+ Some("typc") => crate::syntax::parse_code(&self.text),
+ _ => crate::syntax::parse(&self.text),
};
let mut seq = vec![];
diff --git a/src/library/utility/mod.rs b/src/library/utility/mod.rs
index d9b19d64..2d637d29 100644
--- a/src/library/utility/mod.rs
+++ b/src/library/utility/mod.rs
@@ -12,9 +12,9 @@ pub use string::*;
use comemo::Track;
-use crate::eval::{Eval, Route, Scopes, Vm};
use crate::library::prelude::*;
-use crate::source::Source;
+use crate::model::{Eval, Route, Scopes, Vm};
+use crate::syntax::Source;
/// The name of a value's type.
pub fn type_(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
diff --git a/src/library/utility/string.rs b/src/library/utility/string.rs
index 91a990a9..66f127d1 100644
--- a/src/library/utility/string.rs
+++ b/src/library/utility/string.rs
@@ -1,5 +1,5 @@
-use crate::eval::Regex;
use crate::library::prelude::*;
+use crate::model::Regex;
/// The string representation of a value.
pub fn repr(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
diff --git a/src/main.rs b/src/main.rs
index ef441091..e32bb8c6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -21,7 +21,7 @@ use walkdir::WalkDir;
use typst::diag::{FileError, FileResult, SourceError, StrResult};
use typst::font::{Font, FontBook, FontInfo, FontVariant};
-use typst::source::{Source, SourceId};
+use typst::syntax::{Source, SourceId};
use typst::util::{Buffer, PathExt};
use typst::{Config, World};
diff --git a/src/eval/args.rs b/src/model/args.rs
index f95fbf08..f95fbf08 100644
--- a/src/eval/args.rs
+++ b/src/model/args.rs
diff --git a/src/eval/array.rs b/src/model/array.rs
index b77ce93c..196f02ec 100644
--- a/src/eval/array.rs
+++ b/src/model/array.rs
@@ -12,11 +12,11 @@ use crate::util::ArcExt;
#[allow(unused_macros)]
macro_rules! array {
($value:expr; $count:expr) => {
- $crate::eval::Array::from_vec(vec![$value.into(); $count])
+ $crate::model::Array::from_vec(vec![$value.into(); $count])
};
($($value:expr),* $(,)?) => {
- $crate::eval::Array::from_vec(vec![$($value.into()),*])
+ $crate::model::Array::from_vec(vec![$($value.into()),*])
};
}
diff --git a/src/eval/capture.rs b/src/model/capture.rs
index 289d31e1..c4c107b2 100644
--- a/src/eval/capture.rs
+++ b/src/model/capture.rs
@@ -133,7 +133,7 @@ impl<'a> CapturesVisitor<'a> {
#[cfg(test)]
mod tests {
use super::*;
- use crate::parse::parse;
+ use crate::syntax::parse;
#[track_caller]
fn test(text: &str, result: &[&str]) {
diff --git a/src/eval/cast.rs b/src/model/cast.rs
index 99c34810..00a3fe45 100644
--- a/src/eval/cast.rs
+++ b/src/model/cast.rs
@@ -1,9 +1,8 @@
use std::num::NonZeroUsize;
-use super::{Regex, Value};
+use super::{Content, Layout, LayoutNode, Pattern, Regex, Value};
use crate::diag::{with_alternative, StrResult};
use crate::geom::{Corners, Dir, Paint, Sides};
-use crate::model::{Content, Layout, LayoutNode, Pattern};
use crate::syntax::Spanned;
use crate::util::EcoString;
@@ -19,20 +18,20 @@ pub trait Cast<V = Value>: Sized {
/// Implement traits for dynamic types.
macro_rules! dynamic {
($type:ty: $name:literal, $($tts:tt)*) => {
- impl $crate::eval::Type for $type {
+ impl $crate::model::Type for $type {
const TYPE_NAME: &'static str = $name;
}
castable! {
$type,
- Expected: <Self as $crate::eval::Type>::TYPE_NAME,
+ Expected: <Self as $crate::model::Type>::TYPE_NAME,
$($tts)*
@this: Self => this.clone(),
}
- impl From<$type> for $crate::eval::Value {
+ impl From<$type> for $crate::model::Value {
fn from(v: $type) -> Self {
- $crate::eval::Value::Dyn($crate::eval::Dynamic::new(v))
+ $crate::model::Value::Dyn($crate::model::Dynamic::new(v))
}
}
};
@@ -41,12 +40,12 @@ macro_rules! dynamic {
/// Make a type castable from a value.
macro_rules! castable {
($type:ty: $inner:ty) => {
- impl $crate::eval::Cast<$crate::eval::Value> for $type {
- fn is(value: &$crate::eval::Value) -> bool {
+ impl $crate::model::Cast<$crate::model::Value> for $type {
+ fn is(value: &$crate::model::Value) -> bool {
<$inner>::is(value)
}
- fn cast(value: $crate::eval::Value) -> $crate::diag::StrResult<Self> {
+ fn cast(value: $crate::model::Value) -> $crate::diag::StrResult<Self> {
<$inner>::cast(value).map(Self)
}
}
@@ -59,22 +58,22 @@ macro_rules! castable {
$(@$dyn_in:ident: $dyn_type:ty => $dyn_out:expr,)*
) => {
#[allow(unreachable_patterns)]
- impl $crate::eval::Cast<$crate::eval::Value> for $type {
- fn is(value: &$crate::eval::Value) -> bool {
+ impl $crate::model::Cast<$crate::model::Value> for $type {
+ fn is(value: &$crate::model::Value) -> bool {
#[allow(unused_variables)]
match value {
$($pattern => true,)*
- $crate::eval::Value::Dyn(dynamic) => {
+ $crate::model::Value::Dyn(dynamic) => {
false $(|| dynamic.is::<$dyn_type>())*
}
_ => false,
}
}
- fn cast(value: $crate::eval::Value) -> $crate::diag::StrResult<Self> {
+ fn cast(value: $crate::model::Value) -> $crate::diag::StrResult<Self> {
let found = match value {
$($pattern => return Ok($out),)*
- $crate::eval::Value::Dyn(dynamic) => {
+ $crate::model::Value::Dyn(dynamic) => {
$(if let Some($dyn_in) = dynamic.downcast::<$dyn_type>() {
return Ok($dyn_out);
})*
diff --git a/src/model/content.rs b/src/model/content.rs
index 7828a3cd..5f0536c3 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -21,21 +21,6 @@ use crate::library::text::{
use crate::util::EcoString;
use crate::World;
-/// Layout content into a collection of pages.
-///
-/// Relayouts until all pinned locations are converged.
-#[comemo::memoize]
-pub fn layout(world: Tracked<dyn World>, content: &Content) -> SourceResult<Vec<Frame>> {
- let styles = StyleChain::with_root(&world.config().styles);
- let scratch = Scratch::default();
-
- let mut builder = Builder::new(world, &scratch, true);
- builder.accept(content, styles)?;
-
- let (doc, shared) = builder.into_doc(styles)?;
- doc.layout(world, shared)
-}
-
/// Composable representation of styled content.
///
/// This results from:
@@ -332,7 +317,7 @@ impl Sum for Content {
}
/// Builds a document or a flow node from content.
-struct Builder<'a> {
+pub(super) struct Builder<'a> {
/// The core context.
world: Tracked<'a, dyn World>,
/// Scratch arenas for building.
@@ -349,7 +334,7 @@ struct Builder<'a> {
/// Temporary storage arenas for building.
#[derive(Default)]
-struct Scratch<'a> {
+pub(super) struct Scratch<'a> {
/// An arena where intermediate style chains are stored.
styles: Arena<StyleChain<'a>>,
/// An arena where intermediate content resulting from show rules is stored.
@@ -357,7 +342,11 @@ struct Scratch<'a> {
}
impl<'a> Builder<'a> {
- fn new(world: Tracked<'a, dyn World>, scratch: &'a Scratch<'a>, top: bool) -> Self {
+ pub fn new(
+ world: Tracked<'a, dyn World>,
+ scratch: &'a Scratch<'a>,
+ top: bool,
+ ) -> Self {
Self {
world,
scratch,
@@ -368,7 +357,7 @@ impl<'a> Builder<'a> {
}
}
- fn into_doc(
+ pub fn into_doc(
mut self,
styles: StyleChain<'a>,
) -> SourceResult<(DocNode, StyleChain<'a>)> {
@@ -377,7 +366,7 @@ impl<'a> Builder<'a> {
Ok((DocNode(pages), shared))
}
- fn into_flow(
+ pub fn into_flow(
mut self,
styles: StyleChain<'a>,
) -> SourceResult<(FlowNode, StyleChain<'a>)> {
@@ -386,7 +375,7 @@ impl<'a> Builder<'a> {
Ok((FlowNode(children), shared))
}
- fn accept(
+ pub fn accept(
&mut self,
content: &'a Content,
styles: StyleChain<'a>,
diff --git a/src/eval/dict.rs b/src/model/dict.rs
index b95ead31..3e4fd956 100644
--- a/src/eval/dict.rs
+++ b/src/model/dict.rs
@@ -5,7 +5,7 @@ use std::sync::Arc;
use super::{Args, Array, Func, Str, Value, Vm};
use crate::diag::{SourceResult, StrResult};
-use crate::parse::is_ident;
+use crate::syntax::is_ident;
use crate::syntax::Spanned;
use crate::util::ArcExt;
@@ -16,7 +16,7 @@ macro_rules! dict {
#[allow(unused_mut)]
let mut map = std::collections::BTreeMap::new();
$(map.insert($key.into(), $value.into());)*
- $crate::eval::Dict::from_map(map)
+ $crate::model::Dict::from_map(map)
}};
}
diff --git a/src/eval/mod.rs b/src/model/eval.rs
index 0a3d6545..aa5f0378 100644
--- a/src/eval/mod.rs
+++ b/src/model/eval.rs
@@ -1,50 +1,20 @@
//! Evaluation of markup into modules.
-#[macro_use]
-mod cast;
-#[macro_use]
-mod array;
-#[macro_use]
-mod dict;
-#[macro_use]
-mod str;
-#[macro_use]
-mod value;
-mod args;
-mod capture;
-mod func;
-pub mod methods;
-pub mod ops;
-mod raw;
-mod scope;
-mod vm;
-
-pub use self::str::*;
-pub use args::*;
-pub use array::*;
-pub use capture::*;
-pub use cast::*;
-pub use dict::*;
-pub use func::*;
-pub use raw::*;
-pub use scope::*;
-pub use typst_macros::node;
-pub use value::*;
-pub use vm::*;
-
use std::collections::BTreeMap;
use std::sync::Arc;
use comemo::{Track, Tracked};
use unicode_segmentation::UnicodeSegmentation;
+use super::{
+ methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Flow, Func,
+ Pattern, Recipe, Scope, Scopes, StyleEntry, StyleMap, Value, Vm,
+};
use crate::diag::{At, SourceResult, StrResult, Trace, Tracepoint};
use crate::geom::{Angle, Em, Fraction, Length, Ratio};
use crate::library;
-use crate::model::{Content, Pattern, Recipe, StyleEntry, StyleMap};
-use crate::source::SourceId;
use crate::syntax::ast::TypedNode;
-use crate::syntax::{ast, Span, Spanned, Unit};
+use crate::syntax::{ast, SourceId, Span, Spanned, Unit};
use crate::util::EcoString;
use crate::World;
diff --git a/src/eval/func.rs b/src/model/func.rs
index c307b237..a4f63aa1 100644
--- a/src/eval/func.rs
+++ b/src/model/func.rs
@@ -4,11 +4,12 @@ use std::sync::Arc;
use comemo::{Track, Tracked};
-use super::{Args, Eval, Flow, Route, Scope, Scopes, Value, Vm};
+use super::{
+ Args, Content, Eval, Flow, NodeId, Route, Scope, Scopes, StyleMap, Value, Vm,
+};
use crate::diag::{SourceResult, StrResult};
-use crate::model::{Content, NodeId, StyleMap};
-use crate::source::SourceId;
use crate::syntax::ast::Expr;
+use crate::syntax::SourceId;
use crate::util::EcoString;
use crate::World;
diff --git a/src/model/layout.rs b/src/model/layout.rs
index 8064afff..09888ba5 100644
--- a/src/model/layout.rs
+++ b/src/model/layout.rs
@@ -8,8 +8,8 @@ use std::sync::Arc;
use comemo::{Prehashed, Tracked};
use super::{Barrier, NodeId, Resolve, StyleChain, StyleEntry};
+use super::{Builder, Content, RawAlign, RawLength, Scratch};
use crate::diag::SourceResult;
-use crate::eval::{RawAlign, RawLength};
use crate::frame::{Element, Frame};
use crate::geom::{
Align, Geometry, Length, Paint, Point, Relative, Sides, Size, Spec, Stroke,
@@ -18,6 +18,21 @@ use crate::library::graphics::MoveNode;
use crate::library::layout::{AlignNode, PadNode};
use crate::World;
+/// Layout content into a collection of pages.
+///
+/// Relayouts until all pinned locations are converged.
+#[comemo::memoize]
+pub fn layout(world: Tracked<dyn World>, content: &Content) -> SourceResult<Vec<Frame>> {
+ let styles = StyleChain::with_root(&world.config().styles);
+ let scratch = Scratch::default();
+
+ let mut builder = Builder::new(world, &scratch, true);
+ builder.accept(content, styles)?;
+
+ let (doc, shared) = builder.into_doc(styles)?;
+ doc.layout(world, shared)
+}
+
/// A node that can be layouted into a sequence of regions.
///
/// Layouting returns one frame per used region.
diff --git a/src/eval/methods.rs b/src/model/methods.rs
index 57fff681..57fff681 100644
--- a/src/eval/methods.rs
+++ b/src/model/methods.rs
diff --git a/src/model/mod.rs b/src/model/mod.rs
index 5c8b82c0..0ea5cbdd 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -1,18 +1,50 @@
-//! Styled and structured representation of layoutable content.
+//! Layout and computation model.
#[macro_use]
mod styles;
mod collapse;
mod content;
+mod eval;
mod layout;
mod property;
mod recipe;
mod show;
+#[macro_use]
+mod cast;
+#[macro_use]
+mod array;
+#[macro_use]
+mod dict;
+#[macro_use]
+mod str;
+#[macro_use]
+mod value;
+mod args;
+mod capture;
+mod func;
+pub mod methods;
+pub mod ops;
+mod raw;
+mod scope;
+mod vm;
+pub use self::str::*;
+pub use args::*;
+pub use array::*;
+pub use capture::*;
+pub use cast::*;
pub use collapse::*;
pub use content::*;
+pub use dict::*;
+pub use eval::*;
+pub use func::*;
pub use layout::*;
pub use property::*;
+pub use raw::*;
pub use recipe::*;
+pub use scope::*;
pub use show::*;
pub use styles::*;
+pub use typst_macros::node;
+pub use value::*;
+pub use vm::*;
diff --git a/src/eval/ops.rs b/src/model/ops.rs
index 7e465320..c521f704 100644
--- a/src/eval/ops.rs
+++ b/src/model/ops.rs
@@ -5,7 +5,6 @@ use std::cmp::Ordering;
use super::{RawAlign, RawLength, RawStroke, Regex, Smart, Value};
use crate::diag::StrResult;
use crate::geom::{Numeric, Relative, Spec, SpecAxis};
-use crate::model;
use Value::*;
/// Bail with a type mismatch error.
@@ -21,8 +20,8 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
(a, None) => a,
(None, b) => b,
(Str(a), Str(b)) => Str(a + b),
- (Str(a), Content(b)) => Content(model::Content::Text(a.into()) + b),
- (Content(a), Str(b)) => Content(a + model::Content::Text(b.into())),
+ (Str(a), Content(b)) => Content(super::Content::Text(a.into()) + b),
+ (Content(a), Str(b)) => Content(a + super::Content::Text(b.into())),
(Content(a), Content(b)) => Content(a + b),
(Array(a), Array(b)) => Array(a + b),
(Dict(a), Dict(b)) => Dict(a + b),
@@ -87,8 +86,8 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
(Str(a), Str(b)) => Str(a + b),
(Content(a), Content(b)) => Content(a + b),
- (Content(a), Str(b)) => Content(a + model::Content::Text(b.into())),
- (Str(a), Content(b)) => Content(model::Content::Text(a.into()) + b),
+ (Content(a), Str(b)) => Content(a + super::Content::Text(b.into())),
+ (Str(a), Content(b)) => Content(super::Content::Text(a.into()) + b),
(Array(a), Array(b)) => Array(a + b),
(Dict(a), Dict(b)) => Dict(a + b),
diff --git a/src/model/property.rs b/src/model/property.rs
index ab4f02e3..ed2ab1d0 100644
--- a/src/model/property.rs
+++ b/src/model/property.rs
@@ -5,8 +5,7 @@ use std::sync::Arc;
use comemo::Prehashed;
-use super::{Interruption, NodeId, StyleChain};
-use crate::eval::{RawLength, Smart};
+use super::{Interruption, NodeId, RawLength, Smart, StyleChain};
use crate::geom::{Corners, Length, Numeric, Relative, Sides, Spec};
use crate::library::layout::PageNode;
use crate::library::structure::{DescNode, EnumNode, ListNode};
diff --git a/src/eval/raw.rs b/src/model/raw.rs
index 9cf346b1..b40a88ec 100644
--- a/src/eval/raw.rs
+++ b/src/model/raw.rs
@@ -2,12 +2,11 @@ use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter};
use std::ops::{Add, Div, Mul, Neg};
-use super::{Smart, Value};
+use super::{Fold, Resolve, Smart, StyleChain, Value};
use crate::geom::{
Align, Em, Get, Length, Numeric, Paint, Relative, Spec, SpecAxis, Stroke,
};
use crate::library::text::TextNode;
-use crate::model::{Fold, Resolve, StyleChain};
/// The unresolved alignment representation.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
diff --git a/src/model/recipe.rs b/src/model/recipe.rs
index 27b1be42..05ef07a6 100644
--- a/src/model/recipe.rs
+++ b/src/model/recipe.rs
@@ -2,9 +2,11 @@ use std::fmt::{self, Debug, Formatter};
use comemo::Tracked;
-use super::{Content, Interruption, NodeId, Show, ShowNode, StyleChain, StyleEntry};
+use super::{
+ Args, Content, Func, Interruption, NodeId, Regex, Show, ShowNode, StyleChain,
+ StyleEntry, Value,
+};
use crate::diag::SourceResult;
-use crate::eval::{Args, Func, Regex, Value};
use crate::library::structure::{DescNode, EnumNode, ListNode};
use crate::syntax::Spanned;
use crate::World;
diff --git a/src/eval/scope.rs b/src/model/scope.rs
index 1ab7032c..1ab7032c 100644
--- a/src/eval/scope.rs
+++ b/src/model/scope.rs
diff --git a/src/model/show.rs b/src/model/show.rs
index b30b2264..bff69448 100644
--- a/src/model/show.rs
+++ b/src/model/show.rs
@@ -4,9 +4,8 @@ use std::sync::Arc;
use comemo::{Prehashed, Tracked};
-use super::{Content, NodeId, Selector, StyleChain};
+use super::{Content, Dict, NodeId, Selector, StyleChain};
use crate::diag::SourceResult;
-use crate::eval::Dict;
use crate::World;
/// A node that can be realized given some styles.
diff --git a/src/eval/str.rs b/src/model/str.rs
index 9d2375d3..62b37845 100644
--- a/src/eval/str.rs
+++ b/src/model/str.rs
@@ -13,7 +13,7 @@ use crate::util::EcoString;
#[allow(unused_macros)]
macro_rules! format_str {
($($tts:tt)*) => {{
- $crate::eval::Str::from(format_eco!($($tts)*))
+ $crate::model::Str::from(format_eco!($($tts)*))
}};
}
@@ -76,69 +76,69 @@ impl Str {
}
/// Whether the given pattern exists in this string.
- pub fn contains(&self, pattern: TextPattern) -> bool {
+ pub fn contains(&self, pattern: StrPattern) -> bool {
match pattern {
- TextPattern::Str(pat) => self.0.contains(pat.as_str()),
- TextPattern::Regex(re) => re.is_match(self),
+ StrPattern::Str(pat) => self.0.contains(pat.as_str()),
+ StrPattern::Regex(re) => re.is_match(self),
}
}
/// Whether this string begins with the given pattern.
- pub fn starts_with(&self, pattern: TextPattern) -> bool {
+ pub fn starts_with(&self, pattern: StrPattern) -> bool {
match pattern {
- TextPattern::Str(pat) => self.0.starts_with(pat.as_str()),
- TextPattern::Regex(re) => re.find(self).map_or(false, |m| m.start() == 0),
+ StrPattern::Str(pat) => self.0.starts_with(pat.as_str()),
+ StrPattern::Regex(re) => re.find(self).map_or(false, |m| m.start() == 0),
}
}
/// Whether this string ends with the given pattern.
- pub fn ends_with(&self, pattern: TextPattern) -> bool {
+ pub fn ends_with(&self, pattern: StrPattern) -> bool {
match pattern {
- TextPattern::Str(pat) => self.0.ends_with(pat.as_str()),
- TextPattern::Regex(re) => {
+ StrPattern::Str(pat) => self.0.ends_with(pat.as_str()),
+ StrPattern::Regex(re) => {
re.find_iter(self).last().map_or(false, |m| m.end() == self.0.len())
}
}
}
/// The text of the pattern's first match in this string.
- pub fn find(&self, pattern: TextPattern) -> Option<Self> {
+ pub fn find(&self, pattern: StrPattern) -> Option<Self> {
match pattern {
- TextPattern::Str(pat) => self.0.contains(pat.as_str()).then(|| pat),
- TextPattern::Regex(re) => re.find(self).map(|m| m.as_str().into()),
+ StrPattern::Str(pat) => self.0.contains(pat.as_str()).then(|| pat),
+ StrPattern::Regex(re) => re.find(self).map(|m| m.as_str().into()),
}
}
/// The position of the pattern's first match in this string.
- pub fn position(&self, pattern: TextPattern) -> Option<i64> {
+ pub fn position(&self, pattern: StrPattern) -> Option<i64> {
match pattern {
- TextPattern::Str(pat) => self.0.find(pat.as_str()).map(|i| i as i64),
- TextPattern::Regex(re) => re.find(self).map(|m| m.start() as i64),
+ StrPattern::Str(pat) => self.0.find(pat.as_str()).map(|i| i as i64),
+ StrPattern::Regex(re) => re.find(self).map(|m| m.start() as i64),
}
}
/// The start and, text and capture groups (if any) of the first match of
/// the pattern in this string.
- pub fn match_(&self, pattern: TextPattern) -> Option<Dict> {
+ pub fn match_(&self, pattern: StrPattern) -> Option<Dict> {
match pattern {
- TextPattern::Str(pat) => {
+ StrPattern::Str(pat) => {
self.0.match_indices(pat.as_str()).next().map(match_to_dict)
}
- TextPattern::Regex(re) => re.captures(self).map(captures_to_dict),
+ StrPattern::Regex(re) => re.captures(self).map(captures_to_dict),
}
}
/// The start, end, text and capture groups (if any) of all matches of the
/// pattern in this string.
- pub fn matches(&self, pattern: TextPattern) -> Array {
+ pub fn matches(&self, pattern: StrPattern) -> Array {
match pattern {
- TextPattern::Str(pat) => self
+ StrPattern::Str(pat) => self
.0
.match_indices(pat.as_str())
.map(match_to_dict)
.map(Value::Dict)
.collect(),
- TextPattern::Regex(re) => re
+ StrPattern::Regex(re) => re
.captures_iter(self)
.map(captures_to_dict)
.map(Value::Dict)
@@ -147,14 +147,14 @@ impl Str {
}
/// Split this string at whitespace or a specific pattern.
- pub fn split(&self, pattern: Option<TextPattern>) -> Array {
+ pub fn split(&self, pattern: Option<StrPattern>) -> Array {
let s = self.as_str();
match pattern {
None => s.split_whitespace().map(|v| Value::Str(v.into())).collect(),
- Some(TextPattern::Str(pat)) => {
+ Some(StrPattern::Str(pat)) => {
s.split(pat.as_str()).map(|v| Value::Str(v.into())).collect()
}
- Some(TextPattern::Regex(re)) => {
+ Some(StrPattern::Regex(re)) => {
re.split(s).map(|v| Value::Str(v.into())).collect()
}
}
@@ -166,20 +166,20 @@ impl Str {
/// pattern.
pub fn trim(
&self,
- pattern: Option<TextPattern>,
- at: Option<TextSide>,
+ pattern: Option<StrPattern>,
+ at: Option<StrSide>,
repeat: bool,
) -> Self {
- let mut start = matches!(at, Some(TextSide::Start) | None);
- let end = matches!(at, Some(TextSide::End) | None);
+ let mut start = matches!(at, Some(StrSide::Start) | None);
+ let end = matches!(at, Some(StrSide::End) | None);
let trimmed = match pattern {
None => match at {
None => self.0.trim(),
- Some(TextSide::Start) => self.0.trim_start(),
- Some(TextSide::End) => self.0.trim_end(),
+ Some(StrSide::Start) => self.0.trim_start(),
+ Some(StrSide::End) => self.0.trim_end(),
},
- Some(TextPattern::Str(pat)) => {
+ Some(StrPattern::Str(pat)) => {
let pat = pat.as_str();
let mut s = self.as_str();
if repeat {
@@ -199,7 +199,7 @@ impl Str {
}
s
}
- Some(TextPattern::Regex(re)) => {
+ Some(StrPattern::Regex(re)) => {
let s = self.as_str();
let mut last = 0;
let mut range = 0 .. s.len();
@@ -239,18 +239,13 @@ impl Str {
/// Replace at most `count` occurances of the given pattern with a
/// replacement string (beginning from the start).
- pub fn replace(
- &self,
- pattern: TextPattern,
- with: Self,
- count: Option<usize>,
- ) -> Self {
+ pub fn replace(&self, pattern: StrPattern, with: Self, count: Option<usize>) -> Self {
match pattern {
- TextPattern::Str(pat) => match count {
+ StrPattern::Str(pat) => match count {
Some(n) => self.0.replacen(pat.as_str(), &with, n).into(),
None => self.0.replace(pat.as_str(), &with).into(),
},
- TextPattern::Regex(re) => match count {
+ StrPattern::Regex(re) => match count {
Some(n) => re.replacen(self, n, with.as_str()).into(),
None => re.replace(self, with.as_str()).into(),
},
@@ -440,7 +435,7 @@ impl Hash for Regex {
/// A pattern which can be searched for in a string.
#[derive(Debug, Clone)]
-pub enum TextPattern {
+pub enum StrPattern {
/// Just a string.
Str(Str),
/// A regular expression.
@@ -448,7 +443,7 @@ pub enum TextPattern {
}
castable! {
- TextPattern,
+ StrPattern,
Expected: "string or regular expression",
Value::Str(text) => Self::Str(text),
@regex: Regex => Self::Regex(regex.clone()),
@@ -456,7 +451,7 @@ castable! {
/// A side of a string.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum TextSide {
+pub enum StrSide {
/// The logical start of the string, may be left or right depending on the
/// language.
Start,
@@ -465,7 +460,7 @@ pub enum TextSide {
}
castable! {
- TextSide,
+ StrSide,
Expected: "start or end",
@align: RawAlign => match align {
RawAlign::Start => Self::Start,
diff --git a/src/model/styles.rs b/src/model/styles.rs
index 93b615fc..76199ca1 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -107,7 +107,7 @@ impl StyleMap {
/// Mark all contained properties as _scoped_. This means that they only
/// apply to the first descendant node (of their type) in the hierarchy and
/// not its children, too. This is used by
- /// [constructors](crate::eval::Node::construct).
+ /// [constructors](super::Node::construct).
pub fn scoped(mut self) -> Self {
for entry in &mut self.0 {
if let StyleEntry::Property(property) = entry {
diff --git a/src/eval/value.rs b/src/model/value.rs
index b7bd6d3c..4075ce9c 100644
--- a/src/eval/value.rs
+++ b/src/model/value.rs
@@ -4,11 +4,12 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
-use super::{ops, Args, Array, Cast, Dict, Func, RawLength, Str};
+use siphasher::sip128::{Hasher128, SipHasher};
+
+use super::{ops, Args, Array, Cast, Content, Dict, Func, Layout, RawLength, Str};
use crate::diag::StrResult;
use crate::geom::{Angle, Color, Em, Fraction, Length, Ratio, Relative, RgbaColor};
use crate::library::text::RawNode;
-use crate::model::{Content, Layout};
use crate::util::EcoString;
/// A computational value.
@@ -296,7 +297,7 @@ trait Bounds: Debug + Sync + Send + 'static {
fn as_any(&self) -> &dyn Any;
fn dyn_eq(&self, other: &Dynamic) -> bool;
fn dyn_type_name(&self) -> &'static str;
- fn hash64(&self) -> u64;
+ fn hash128(&self) -> u128;
}
impl<T> Bounds for T
@@ -319,19 +320,19 @@ where
T::TYPE_NAME
}
- fn hash64(&self) -> u64 {
+ fn hash128(&self) -> u128 {
// Also hash the TypeId since nodes with different types but
// equal data should be different.
- let mut state = fxhash::FxHasher64::default();
+ let mut state = SipHasher::new();
self.type_id().hash(&mut state);
self.hash(&mut state);
- state.finish()
+ state.finish128().as_u128()
}
}
impl Hash for dyn Bounds {
fn hash<H: Hasher>(&self, state: &mut H) {
- state.write_u64(self.hash64());
+ state.write_u128(self.hash128());
}
}
diff --git a/src/eval/vm.rs b/src/model/vm.rs
index 0604e7be..a1b1ba81 100644
--- a/src/eval/vm.rs
+++ b/src/model/vm.rs
@@ -4,8 +4,7 @@ use comemo::Tracked;
use super::{Route, Scopes, Value};
use crate::diag::{SourceError, StrResult};
-use crate::source::SourceId;
-use crate::syntax::Span;
+use crate::syntax::{SourceId, Span};
use crate::util::PathExt;
use crate::World;
diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs
index bfb36078..325b7274 100644
--- a/src/syntax/highlight.rs
+++ b/src/syntax/highlight.rs
@@ -6,7 +6,7 @@ use std::ops::Range;
use syntect::highlighting::{Color, FontStyle, Highlighter, Style, Theme};
use syntect::parsing::Scope;
-use super::{NodeKind, SyntaxNode};
+use super::{parse, NodeKind, SyntaxNode};
/// Highlight source text into a standalone HTML document.
pub fn highlight_html(text: &str, theme: &Theme) -> String {
@@ -28,7 +28,7 @@ pub fn highlight_pre(text: &str, theme: &Theme) -> String {
let mut buf = String::new();
buf.push_str("<pre>\n");
- let root = crate::parse::parse(text);
+ let root = parse(text);
highlight_themed(&root, theme, |range, style| {
let styled = style != Style::default();
if styled {
@@ -401,8 +401,8 @@ impl Category {
#[cfg(test)]
mod tests {
+ use super::super::Source;
use super::*;
- use crate::source::Source;
#[test]
fn test_highlighting() {
diff --git a/src/parse/incremental.rs b/src/syntax/incremental.rs
index 4651a784..529defd7 100644
--- a/src/parse/incremental.rs
+++ b/src/syntax/incremental.rs
@@ -1,10 +1,9 @@
use std::ops::Range;
use std::sync::Arc;
-use crate::syntax::{InnerNode, NodeKind, Span, SyntaxNode};
-
use super::{
- is_newline, parse, reparse_code_block, reparse_content_block, reparse_markup_elements,
+ is_newline, parse, reparse_code_block, reparse_content_block,
+ reparse_markup_elements, InnerNode, NodeKind, Span, SyntaxNode,
};
/// Refresh the given syntax node with as little parsing as possible.
@@ -413,9 +412,8 @@ fn next_at_start(kind: &NodeKind, prev: bool) -> bool {
#[rustfmt::skip]
mod tests {
use super::*;
- use crate::parse::parse;
- use crate::parse::tests::check;
- use crate::source::Source;
+ use super::super::{parse, Source};
+ use super::super::tests::check;
#[track_caller]
fn test(prev: &str, range: Range<usize>, with: &str, goal: Range<usize>) {
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 8b172def..1a23db5f 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -1,558 +1,41 @@
-//! Syntax types.
+//! Syntax definition, parsing, and highlighting.
pub mod ast;
pub mod highlight;
+mod incremental;
mod kind;
+mod node;
+mod parser;
+mod parsing;
+mod resolve;
+mod source;
mod span;
-
-use std::fmt::{self, Debug, Formatter};
-use std::ops::Range;
-use std::sync::Arc;
+mod tokens;
pub use kind::*;
+pub use node::*;
+pub use parsing::*;
+pub use source::*;
pub use span::*;
+pub use tokens::*;
-use self::ast::TypedNode;
-use crate::diag::SourceError;
-use crate::source::SourceId;
-
-/// An inner or leaf node in the untyped syntax tree.
-#[derive(Clone, PartialEq, Hash)]
-pub enum SyntaxNode {
- /// A reference-counted inner node.
- Inner(Arc<InnerNode>),
- /// A leaf token.
- Leaf(NodeData),
-}
-
-impl SyntaxNode {
- /// The metadata of the node.
- pub fn data(&self) -> &NodeData {
- match self {
- Self::Inner(inner) => &inner.data,
- Self::Leaf(leaf) => leaf,
- }
- }
-
- /// The type of the node.
- pub fn kind(&self) -> &NodeKind {
- self.data().kind()
- }
-
- /// The length of the node.
- pub fn len(&self) -> usize {
- self.data().len()
- }
-
- /// The number of descendants, including the node itself.
- pub fn descendants(&self) -> usize {
- match self {
- Self::Inner(inner) => inner.descendants(),
- Self::Leaf(_) => 1,
- }
- }
-
- /// The span of the node.
- pub fn span(&self) -> Span {
- self.data().span()
- }
-
- /// Whether the node or its children contain an error.
- pub fn erroneous(&self) -> bool {
- match self {
- Self::Inner(node) => node.erroneous,
- Self::Leaf(data) => data.kind.is_error(),
- }
- }
+use incremental::reparse;
+use parser::*;
- /// The error messages for this node and its descendants.
- pub fn errors(&self) -> Vec<SourceError> {
- if !self.erroneous() {
- return vec![];
- }
+#[cfg(test)]
+mod tests {
+ use std::fmt::Debug;
- match self.kind() {
- NodeKind::Error(pos, message) => {
- vec![SourceError::new(self.span(), message.clone()).with_pos(*pos)]
- }
- _ => self
- .children()
- .filter(|node| node.erroneous())
- .flat_map(|node| node.errors())
- .collect(),
- }
- }
-
- /// The node's children.
- pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
- match self {
- Self::Inner(inner) => inner.children(),
- Self::Leaf(_) => [].iter(),
- }
- }
-
- /// Convert the node to a typed AST node.
- pub fn cast<T>(&self) -> Option<T>
+ #[track_caller]
+ pub fn check<T>(text: &str, found: T, expected: T)
where
- T: TypedNode,
+ T: Debug + PartialEq,
{
- T::from_untyped(self)
- }
-
- /// Get the first child that can cast to the AST type `T`.
- pub fn cast_first_child<T: TypedNode>(&self) -> Option<T> {
- self.children().find_map(Self::cast)
- }
-
- /// Get the last child that can cast to the AST type `T`.
- pub fn cast_last_child<T: TypedNode>(&self) -> Option<T> {
- self.children().rev().find_map(Self::cast)
- }
-
- /// Change the type of the node.
- pub fn convert(&mut self, kind: NodeKind) {
- match self {
- Self::Inner(inner) => {
- let node = Arc::make_mut(inner);
- node.erroneous |= kind.is_error();
- node.data.kind = kind;
- }
- Self::Leaf(leaf) => leaf.kind = kind,
- }
- }
-
- /// Set a synthetic span for the node and all its descendants.
- pub fn synthesize(&mut self, span: Span) {
- match self {
- Self::Inner(inner) => Arc::make_mut(inner).synthesize(span),
- Self::Leaf(leaf) => leaf.synthesize(span),
- }
- }
-
- /// Assign spans to each node.
- pub fn numberize(&mut self, id: SourceId, within: Range<u64>) -> NumberingResult {
- match self {
- Self::Inner(inner) => Arc::make_mut(inner).numberize(id, None, within),
- Self::Leaf(leaf) => leaf.numberize(id, within),
- }
- }
-
- /// The upper bound of assigned numbers in this subtree.
- pub fn upper(&self) -> u64 {
- match self {
- Self::Inner(inner) => inner.upper(),
- Self::Leaf(leaf) => leaf.span().number() + 1,
- }
- }
-
- /// If the span points into this node, convert it to a byte range.
- pub fn range(&self, span: Span, offset: usize) -> Option<Range<usize>> {
- match self {
- Self::Inner(inner) => inner.range(span, offset),
- Self::Leaf(leaf) => leaf.range(span, offset),
- }
- }
-
- /// Returns all leaf descendants of this node (may include itself).
- ///
- /// This method is slow and only intended for testing.
- pub fn leafs(&self) -> Vec<Self> {
- if match self {
- Self::Inner(inner) => inner.children.is_empty(),
- Self::Leaf(_) => true,
- } {
- vec![self.clone()]
- } else {
- self.children().flat_map(Self::leafs).collect()
- }
- }
-}
-
-impl Default for SyntaxNode {
- fn default() -> Self {
- Self::Leaf(NodeData::new(NodeKind::None, 0))
- }
-}
-
-impl Debug for SyntaxNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Self::Inner(node) => node.fmt(f),
- Self::Leaf(token) => token.fmt(f),
- }
- }
-}
-
-/// An inner node in the untyped syntax tree.
-#[derive(Clone, Hash)]
-pub struct InnerNode {
- /// Node metadata.
- data: NodeData,
- /// The number of nodes in the whole subtree, including this node.
- descendants: usize,
- /// Whether this node or any of its children are erroneous.
- erroneous: bool,
- /// The upper bound of this node's numbering range.
- upper: u64,
- /// This node's children, losslessly make up this node.
- children: Vec<SyntaxNode>,
-}
-
-impl InnerNode {
- /// Creates a new node with the given kind and a single child.
- pub fn with_child(kind: NodeKind, child: impl Into<SyntaxNode>) -> Self {
- Self::with_children(kind, vec![child.into()])
- }
-
- /// Creates a new node with the given kind and children.
- pub fn with_children(kind: NodeKind, children: Vec<SyntaxNode>) -> Self {
- let mut len = 0;
- let mut descendants = 1;
- let mut erroneous = kind.is_error();
-
- for child in &children {
- len += child.len();
- descendants += child.descendants();
- erroneous |= child.erroneous();
- }
-
- Self {
- data: NodeData::new(kind, len),
- descendants,
- erroneous,
- upper: 0,
- children,
- }
- }
-
- /// The node's metadata.
- pub fn data(&self) -> &NodeData {
- &self.data
- }
-
- /// The node's type.
- pub fn kind(&self) -> &NodeKind {
- self.data().kind()
- }
-
- /// The node's length.
- pub fn len(&self) -> usize {
- self.data().len()
- }
-
- /// The node's span.
- pub fn span(&self) -> Span {
- self.data().span()
- }
-
- /// The number of descendants, including the node itself.
- pub fn descendants(&self) -> usize {
- self.descendants
- }
-
- /// The node's children.
- pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
- self.children.iter()
- }
-
- /// Set a synthetic span for the node and all its descendants.
- pub fn synthesize(&mut self, span: Span) {
- self.data.synthesize(span);
- for child in &mut self.children {
- child.synthesize(span);
- }
- }
-
- /// Assign span numbers `within` an interval to this node's subtree or just
- /// a `range` of its children.
- pub fn numberize(
- &mut self,
- id: SourceId,
- range: Option<Range<usize>>,
- within: Range<u64>,
- ) -> NumberingResult {
- // Determine how many nodes we will number.
- let descendants = match &range {
- Some(range) if range.is_empty() => return Ok(()),
- Some(range) => self.children[range.clone()]
- .iter()
- .map(SyntaxNode::descendants)
- .sum::<usize>(),
- None => self.descendants,
- };
-
- // Determine the distance between two neighbouring assigned numbers. If
- // possible, we try to fit all numbers into the left half of `within`
- // so that there is space for future insertions.
- let space = within.end - within.start;
- let mut stride = space / (2 * descendants as u64);
- if stride == 0 {
- stride = space / self.descendants as u64;
- if stride == 0 {
- return Err(Unnumberable);
- }
- }
-
- // Number this node itself.
- let mut start = within.start;
- if range.is_none() {
- let end = start + stride;
- self.data.numberize(id, start .. end)?;
- self.upper = within.end;
- start = end;
- }
-
- // Number the children.
- let len = self.children.len();
- for child in &mut self.children[range.unwrap_or(0 .. len)] {
- let end = start + child.descendants() as u64 * stride;
- child.numberize(id, start .. end)?;
- start = end;
- }
-
- Ok(())
- }
-
- /// The upper bound of assigned numbers in this subtree.
- pub fn upper(&self) -> u64 {
- self.upper
- }
-
- /// If the span points into this node, convert it to a byte range.
- pub fn range(&self, span: Span, mut offset: usize) -> Option<Range<usize>> {
- // Check whether we found it.
- if let Some(range) = self.data.range(span, offset) {
- return Some(range);
- }
-
- // The parent of a subtree has a smaller span number than all of its
- // descendants. Therefore, we can bail out early if the target span's
- // number is smaller than our number.
- if span.number() < self.span().number() {
- return None;
- }
-
- let mut children = self.children.iter().peekable();
- while let Some(child) = children.next() {
- // Every node in this child's subtree has a smaller span number than
- // the next sibling. Therefore we only need to recurse if the next
- // sibling's span number is larger than the target span's number.
- if children
- .peek()
- .map_or(true, |next| next.span().number() > span.number())
- {
- if let Some(range) = child.range(span, offset) {
- return Some(range);
- }
- }
-
- offset += child.len();
- }
-
- None
- }
-
- /// The node's children, mutably.
- pub(crate) fn children_mut(&mut self) -> &mut [SyntaxNode] {
- &mut self.children
- }
-
- /// Replaces a range of children with a replacement.
- ///
- /// May have mutated the children if it returns `Err(_)`.
- pub(crate) fn replace_children(
- &mut self,
- mut range: Range<usize>,
- replacement: Vec<SyntaxNode>,
- ) -> NumberingResult {
- let superseded = &self.children[range.clone()];
-
- // Compute the new byte length.
- self.data.len = self.data.len
- + replacement.iter().map(SyntaxNode::len).sum::<usize>()
- - superseded.iter().map(SyntaxNode::len).sum::<usize>();
-
- // Compute the new number of descendants.
- self.descendants = self.descendants
- + replacement.iter().map(SyntaxNode::descendants).sum::<usize>()
- - superseded.iter().map(SyntaxNode::descendants).sum::<usize>();
-
- // Determine whether we're still erroneous after the replacement. That's
- // the case if
- // - any of the new nodes is erroneous,
- // - or if we were erroneous before due to a non-superseded node.
- self.erroneous = replacement.iter().any(SyntaxNode::erroneous)
- || (self.erroneous
- && (self.children[.. range.start].iter().any(SyntaxNode::erroneous))
- || self.children[range.end ..].iter().any(SyntaxNode::erroneous));
-
- // Perform the replacement.
- let replacement_count = replacement.len();
- self.children.splice(range.clone(), replacement);
- range.end = range.start + replacement_count;
-
- // Renumber the new children. Retries until it works, taking
- // exponentially more children into account.
- let mut left = 0;
- let mut right = 0;
- let max_left = range.start;
- let max_right = self.children.len() - range.end;
- loop {
- let renumber = range.start - left .. range.end + right;
-
- // The minimum assignable number is either
- // - the upper bound of the node right before the to-be-renumbered
- // children,
- // - or this inner node's span number plus one if renumbering starts
- // at the first child.
- let start_number = renumber
- .start
- .checked_sub(1)
- .and_then(|i| self.children.get(i))
- .map_or(self.span().number() + 1, |child| child.upper());
-
- // The upper bound for renumbering is either
- // - the span number of the first child after the to-be-renumbered
- // children,
- // - or this node's upper bound if renumbering ends behind the last
- // child.
- let end_number = self
- .children
- .get(renumber.end)
- .map_or(self.upper(), |next| next.span().number());
-
- // Try to renumber.
- let within = start_number .. end_number;
- let id = self.span().source();
- if self.numberize(id, Some(renumber), within).is_ok() {
- return Ok(());
- }
-
- // If it didn't even work with all children, we give up.
- if left == max_left && right == max_right {
- return Err(Unnumberable);
- }
-
- // Exponential expansion to both sides.
- left = (left + 1).next_power_of_two().min(max_left);
- right = (right + 1).next_power_of_two().min(max_right);
- }
- }
-
- /// Update this node after changes were made to one of its children.
- pub(crate) fn update_parent(
- &mut self,
- prev_len: usize,
- new_len: usize,
- prev_descendants: usize,
- new_descendants: usize,
- ) {
- self.data.len = self.data.len + new_len - prev_len;
- self.descendants = self.descendants + new_descendants - prev_descendants;
- self.erroneous = self.children.iter().any(SyntaxNode::erroneous);
- }
-}
-
-impl From<InnerNode> for SyntaxNode {
- fn from(node: InnerNode) -> Self {
- Arc::new(node).into()
- }
-}
-
-impl From<Arc<InnerNode>> for SyntaxNode {
- fn from(node: Arc<InnerNode>) -> Self {
- Self::Inner(node)
- }
-}
-
-impl Debug for InnerNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.data.fmt(f)?;
- if !self.children.is_empty() {
- f.write_str(" ")?;
- f.debug_list().entries(&self.children).finish()?;
+ if found != expected {
+ println!("source: {text:?}");
+ println!("expected: {expected:#?}");
+ println!("found: {found:#?}");
+ panic!("test failed");
}
- Ok(())
- }
-}
-
-impl PartialEq for InnerNode {
- fn eq(&self, other: &Self) -> bool {
- self.data == other.data
- && self.descendants == other.descendants
- && self.erroneous == other.erroneous
- && self.children == other.children
- }
-}
-
-/// Data shared between inner and leaf nodes.
-#[derive(Clone, Hash)]
-pub struct NodeData {
- /// 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,
- /// The node's span.
- span: Span,
-}
-
-impl NodeData {
- /// Create new node metadata.
- pub fn new(kind: NodeKind, len: usize) -> Self {
- Self { len, kind, span: Span::detached() }
- }
-
- /// The node's type.
- pub fn kind(&self) -> &NodeKind {
- &self.kind
- }
-
- /// The node's length.
- pub fn len(&self) -> usize {
- self.len
- }
-
- /// The node's span.
- pub fn span(&self) -> Span {
- self.span
- }
-
- /// Set a synthetic span for the node.
- pub fn synthesize(&mut self, span: Span) {
- self.span = span;
- }
-
- /// Assign a span to the node.
- pub fn numberize(&mut self, id: SourceId, within: Range<u64>) -> NumberingResult {
- if within.start < within.end {
- self.span = Span::new(id, (within.start + within.end) / 2);
- Ok(())
- } else {
- Err(Unnumberable)
- }
- }
-
- /// If the span points into this node, convert it to a byte range.
- pub fn range(&self, span: Span, offset: usize) -> Option<Range<usize>> {
- (self.span == span).then(|| offset .. offset + self.len())
- }
-}
-
-impl From<NodeData> for SyntaxNode {
- fn from(token: NodeData) -> Self {
- Self::Leaf(token)
- }
-}
-
-impl Debug for NodeData {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{:?}: {}", self.kind, self.len)
- }
-}
-
-impl PartialEq for NodeData {
- fn eq(&self, other: &Self) -> bool {
- self.kind == other.kind && self.len == other.len
}
}
diff --git a/src/syntax/node.rs b/src/syntax/node.rs
new file mode 100644
index 00000000..6a7d424a
--- /dev/null
+++ b/src/syntax/node.rs
@@ -0,0 +1,548 @@
+use std::fmt::{self, Debug, Formatter};
+use std::ops::Range;
+use std::sync::Arc;
+
+use super::ast::TypedNode;
+use super::{NodeKind, NumberingResult, SourceId, Span, Unnumberable};
+use crate::diag::SourceError;
+
+/// An inner or leaf node in the untyped syntax tree.
+#[derive(Clone, PartialEq, Hash)]
+pub enum SyntaxNode {
+ /// A reference-counted inner node.
+ Inner(Arc<InnerNode>),
+ /// A leaf token.
+ Leaf(NodeData),
+}
+
+impl SyntaxNode {
+ /// The metadata of the node.
+ pub fn data(&self) -> &NodeData {
+ match self {
+ Self::Inner(inner) => &inner.data,
+ Self::Leaf(leaf) => leaf,
+ }
+ }
+
+ /// The type of the node.
+ pub fn kind(&self) -> &NodeKind {
+ self.data().kind()
+ }
+
+ /// The length of the node.
+ pub fn len(&self) -> usize {
+ self.data().len()
+ }
+
+ /// The number of descendants, including the node itself.
+ pub fn descendants(&self) -> usize {
+ match self {
+ Self::Inner(inner) => inner.descendants(),
+ Self::Leaf(_) => 1,
+ }
+ }
+
+ /// The span of the node.
+ pub fn span(&self) -> Span {
+ self.data().span()
+ }
+
+ /// Whether the node or its children contain an error.
+ pub fn erroneous(&self) -> bool {
+ match self {
+ Self::Inner(node) => node.erroneous,
+ Self::Leaf(data) => data.kind.is_error(),
+ }
+ }
+
+ /// The error messages for this node and its descendants.
+ pub fn errors(&self) -> Vec<SourceError> {
+ if !self.erroneous() {
+ return vec![];
+ }
+
+ match self.kind() {
+ NodeKind::Error(pos, message) => {
+ vec![SourceError::new(self.span(), message.clone()).with_pos(*pos)]
+ }
+ _ => self
+ .children()
+ .filter(|node| node.erroneous())
+ .flat_map(|node| node.errors())
+ .collect(),
+ }
+ }
+
+ /// The node's children.
+ pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
+ match self {
+ Self::Inner(inner) => inner.children(),
+ Self::Leaf(_) => [].iter(),
+ }
+ }
+
+ /// Convert the node to a typed AST node.
+ pub fn cast<T>(&self) -> Option<T>
+ where
+ T: TypedNode,
+ {
+ T::from_untyped(self)
+ }
+
+ /// Get the first child that can cast to the AST type `T`.
+ pub fn cast_first_child<T: TypedNode>(&self) -> Option<T> {
+ self.children().find_map(Self::cast)
+ }
+
+ /// Get the last child that can cast to the AST type `T`.
+ pub fn cast_last_child<T: TypedNode>(&self) -> Option<T> {
+ self.children().rev().find_map(Self::cast)
+ }
+
+ /// Change the type of the node.
+ pub fn convert(&mut self, kind: NodeKind) {
+ match self {
+ Self::Inner(inner) => {
+ let node = Arc::make_mut(inner);
+ node.erroneous |= kind.is_error();
+ node.data.kind = kind;
+ }
+ Self::Leaf(leaf) => leaf.kind = kind,
+ }
+ }
+
+ /// Set a synthetic span for the node and all its descendants.
+ pub fn synthesize(&mut self, span: Span) {
+ match self {
+ Self::Inner(inner) => Arc::make_mut(inner).synthesize(span),
+ Self::Leaf(leaf) => leaf.synthesize(span),
+ }
+ }
+
+ /// Assign spans to each node.
+ pub fn numberize(&mut self, id: SourceId, within: Range<u64>) -> NumberingResult {
+ match self {
+ Self::Inner(inner) => Arc::make_mut(inner).numberize(id, None, within),
+ Self::Leaf(leaf) => leaf.numberize(id, within),
+ }
+ }
+
+ /// The upper bound of assigned numbers in this subtree.
+ pub fn upper(&self) -> u64 {
+ match self {
+ Self::Inner(inner) => inner.upper(),
+ Self::Leaf(leaf) => leaf.span().number() + 1,
+ }
+ }
+
+ /// If the span points into this node, convert it to a byte range.
+ pub fn range(&self, span: Span, offset: usize) -> Option<Range<usize>> {
+ match self {
+ Self::Inner(inner) => inner.range(span, offset),
+ Self::Leaf(leaf) => leaf.range(span, offset),
+ }
+ }
+
+ /// Returns all leaf descendants of this node (may include itself).
+ ///
+ /// This method is slow and only intended for testing.
+ pub fn leafs(&self) -> Vec<Self> {
+ if match self {
+ Self::Inner(inner) => inner.children.is_empty(),
+ Self::Leaf(_) => true,
+ } {
+ vec![self.clone()]
+ } else {
+ self.children().flat_map(Self::leafs).collect()
+ }
+ }
+}
+
+impl Default for SyntaxNode {
+ fn default() -> Self {
+ Self::Leaf(NodeData::new(NodeKind::None, 0))
+ }
+}
+
+impl Debug for SyntaxNode {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ Self::Inner(node) => node.fmt(f),
+ Self::Leaf(token) => token.fmt(f),
+ }
+ }
+}
+
+/// An inner node in the untyped syntax tree.
+#[derive(Clone, Hash)]
+pub struct InnerNode {
+ /// Node metadata.
+ data: NodeData,
+ /// The number of nodes in the whole subtree, including this node.
+ descendants: usize,
+ /// Whether this node or any of its children are erroneous.
+ erroneous: bool,
+ /// The upper bound of this node's numbering range.
+ upper: u64,
+ /// This node's children, losslessly make up this node.
+ children: Vec<SyntaxNode>,
+}
+
+impl InnerNode {
+ /// Creates a new node with the given kind and a single child.
+ pub fn with_child(kind: NodeKind, child: impl Into<SyntaxNode>) -> Self {
+ Self::with_children(kind, vec![child.into()])
+ }
+
+ /// Creates a new node with the given kind and children.
+ pub fn with_children(kind: NodeKind, children: Vec<SyntaxNode>) -> Self {
+ let mut len = 0;
+ let mut descendants = 1;
+ let mut erroneous = kind.is_error();
+
+ for child in &children {
+ len += child.len();
+ descendants += child.descendants();
+ erroneous |= child.erroneous();
+ }
+
+ Self {
+ data: NodeData::new(kind, len),
+ descendants,
+ erroneous,
+ upper: 0,
+ children,
+ }
+ }
+
+ /// The node's metadata.
+ pub fn data(&self) -> &NodeData {
+ &self.data
+ }
+
+ /// The node's type.
+ pub fn kind(&self) -> &NodeKind {
+ self.data().kind()
+ }
+
+ /// The node's length.
+ pub fn len(&self) -> usize {
+ self.data().len()
+ }
+
+ /// The node's span.
+ pub fn span(&self) -> Span {
+ self.data().span()
+ }
+
+ /// The number of descendants, including the node itself.
+ pub fn descendants(&self) -> usize {
+ self.descendants
+ }
+
+ /// The node's children.
+ pub fn children(&self) -> std::slice::Iter<'_, SyntaxNode> {
+ self.children.iter()
+ }
+
+ /// Set a synthetic span for the node and all its descendants.
+ pub fn synthesize(&mut self, span: Span) {
+ self.data.synthesize(span);
+ for child in &mut self.children {
+ child.synthesize(span);
+ }
+ }
+
+ /// Assign span numbers `within` an interval to this node's subtree or just
+ /// a `range` of its children.
+ pub fn numberize(
+ &mut self,
+ id: SourceId,
+ range: Option<Range<usize>>,
+ within: Range<u64>,
+ ) -> NumberingResult {
+ // Determine how many nodes we will number.
+ let descendants = match &range {
+ Some(range) if range.is_empty() => return Ok(()),
+ Some(range) => self.children[range.clone()]
+ .iter()
+ .map(SyntaxNode::descendants)
+ .sum::<usize>(),
+ None => self.descendants,
+ };
+
+ // Determine the distance between two neighbouring assigned numbers. If
+ // possible, we try to fit all numbers into the left half of `within`
+ // so that there is space for future insertions.
+ let space = within.end - within.start;
+ let mut stride = space / (2 * descendants as u64);
+ if stride == 0 {
+ stride = space / self.descendants as u64;
+ if stride == 0 {
+ return Err(Unnumberable);
+ }
+ }
+
+ // Number this node itself.
+ let mut start = within.start;
+ if range.is_none() {
+ let end = start + stride;
+ self.data.numberize(id, start .. end)?;
+ self.upper = within.end;
+ start = end;
+ }
+
+ // Number the children.
+ let len = self.children.len();
+ for child in &mut self.children[range.unwrap_or(0 .. len)] {
+ let end = start + child.descendants() as u64 * stride;
+ child.numberize(id, start .. end)?;
+ start = end;
+ }
+
+ Ok(())
+ }
+
+ /// The upper bound of assigned numbers in this subtree.
+ pub fn upper(&self) -> u64 {
+ self.upper
+ }
+
+ /// If the span points into this node, convert it to a byte range.
+ pub fn range(&self, span: Span, mut offset: usize) -> Option<Range<usize>> {
+ // Check whether we found it.
+ if let Some(range) = self.data.range(span, offset) {
+ return Some(range);
+ }
+
+ // The parent of a subtree has a smaller span number than all of its
+ // descendants. Therefore, we can bail out early if the target span's
+ // number is smaller than our number.
+ if span.number() < self.span().number() {
+ return None;
+ }
+
+ let mut children = self.children.iter().peekable();
+ while let Some(child) = children.next() {
+ // Every node in this child's subtree has a smaller span number than
+ // the next sibling. Therefore we only need to recurse if the next
+ // sibling's span number is larger than the target span's number.
+ if children
+ .peek()
+ .map_or(true, |next| next.span().number() > span.number())
+ {
+ if let Some(range) = child.range(span, offset) {
+ return Some(range);
+ }
+ }
+
+ offset += child.len();
+ }
+
+ None
+ }
+
+ /// The node's children, mutably.
+ pub(crate) fn children_mut(&mut self) -> &mut [SyntaxNode] {
+ &mut self.children
+ }
+
+ /// Replaces a range of children with a replacement.
+ ///
+ /// May have mutated the children if it returns `Err(_)`.
+ pub(crate) fn replace_children(
+ &mut self,
+ mut range: Range<usize>,
+ replacement: Vec<SyntaxNode>,
+ ) -> NumberingResult {
+ let superseded = &self.children[range.clone()];
+
+ // Compute the new byte length.
+ self.data.len = self.data.len
+ + replacement.iter().map(SyntaxNode::len).sum::<usize>()
+ - superseded.iter().map(SyntaxNode::len).sum::<usize>();
+
+ // Compute the new number of descendants.
+ self.descendants = self.descendants
+ + replacement.iter().map(SyntaxNode::descendants).sum::<usize>()
+ - superseded.iter().map(SyntaxNode::descendants).sum::<usize>();
+
+ // Determine whether we're still erroneous after the replacement. That's
+ // the case if
+ // - any of the new nodes is erroneous,
+ // - or if we were erroneous before due to a non-superseded node.
+ self.erroneous = replacement.iter().any(SyntaxNode::erroneous)
+ || (self.erroneous
+ && (self.children[.. range.start].iter().any(SyntaxNode::erroneous))
+ || self.children[range.end ..].iter().any(SyntaxNode::erroneous));
+
+ // Perform the replacement.
+ let replacement_count = replacement.len();
+ self.children.splice(range.clone(), replacement);
+ range.end = range.start + replacement_count;
+
+ // Renumber the new children. Retries until it works, taking
+ // exponentially more children into account.
+ let mut left = 0;
+ let mut right = 0;
+ let max_left = range.start;
+ let max_right = self.children.len() - range.end;
+ loop {
+ let renumber = range.start - left .. range.end + right;
+
+ // The minimum assignable number is either
+ // - the upper bound of the node right before the to-be-renumbered
+ // children,
+ // - or this inner node's span number plus one if renumbering starts
+ // at the first child.
+ let start_number = renumber
+ .start
+ .checked_sub(1)
+ .and_then(|i| self.children.get(i))
+ .map_or(self.span().number() + 1, |child| child.upper());
+
+ // The upper bound for renumbering is either
+ // - the span number of the first child after the to-be-renumbered
+ // children,
+ // - or this node's upper bound if renumbering ends behind the last
+ // child.
+ let end_number = self
+ .children
+ .get(renumber.end)
+ .map_or(self.upper(), |next| next.span().number());
+
+ // Try to renumber.
+ let within = start_number .. end_number;
+ let id = self.span().source();
+ if self.numberize(id, Some(renumber), within).is_ok() {
+ return Ok(());
+ }
+
+ // If it didn't even work with all children, we give up.
+ if left == max_left && right == max_right {
+ return Err(Unnumberable);
+ }
+
+ // Exponential expansion to both sides.
+ left = (left + 1).next_power_of_two().min(max_left);
+ right = (right + 1).next_power_of_two().min(max_right);
+ }
+ }
+
+ /// Update this node after changes were made to one of its children.
+ pub(crate) fn update_parent(
+ &mut self,
+ prev_len: usize,
+ new_len: usize,
+ prev_descendants: usize,
+ new_descendants: usize,
+ ) {
+ self.data.len = self.data.len + new_len - prev_len;
+ self.descendants = self.descendants + new_descendants - prev_descendants;
+ self.erroneous = self.children.iter().any(SyntaxNode::erroneous);
+ }
+}
+
+impl From<InnerNode> for SyntaxNode {
+ fn from(node: InnerNode) -> Self {
+ Arc::new(node).into()
+ }
+}
+
+impl From<Arc<InnerNode>> for SyntaxNode {
+ fn from(node: Arc<InnerNode>) -> Self {
+ Self::Inner(node)
+ }
+}
+
+impl Debug for InnerNode {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ self.data.fmt(f)?;
+ if !self.children.is_empty() {
+ f.write_str(" ")?;
+ f.debug_list().entries(&self.children).finish()?;
+ }
+ Ok(())
+ }
+}
+
+impl PartialEq for InnerNode {
+ fn eq(&self, other: &Self) -> bool {
+ self.data == other.data
+ && self.descendants == other.descendants
+ && self.erroneous == other.erroneous
+ && self.children == other.children
+ }
+}
+
+/// Data shared between inner and leaf nodes.
+#[derive(Clone, Hash)]
+pub struct NodeData {
+ /// What kind of node this is (each kind would have its own struct in a
+ /// strongly typed AST).
+ pub(super) kind: NodeKind,
+ /// The byte length of the node in the source.
+ len: usize,
+ /// The node's span.
+ span: Span,
+}
+
+impl NodeData {
+ /// Create new node metadata.
+ pub fn new(kind: NodeKind, len: usize) -> Self {
+ Self { len, kind, span: Span::detached() }
+ }
+
+ /// The node's type.
+ pub fn kind(&self) -> &NodeKind {
+ &self.kind
+ }
+
+ /// The node's length.
+ pub fn len(&self) -> usize {
+ self.len
+ }
+
+ /// The node's span.
+ pub fn span(&self) -> Span {
+ self.span
+ }
+
+ /// Set a synthetic span for the node.
+ pub fn synthesize(&mut self, span: Span) {
+ self.span = span;
+ }
+
+ /// Assign a span to the node.
+ pub fn numberize(&mut self, id: SourceId, within: Range<u64>) -> NumberingResult {
+ if within.start < within.end {
+ self.span = Span::new(id, (within.start + within.end) / 2);
+ Ok(())
+ } else {
+ Err(Unnumberable)
+ }
+ }
+
+ /// If the span points into this node, convert it to a byte range.
+ pub fn range(&self, span: Span, offset: usize) -> Option<Range<usize>> {
+ (self.span == span).then(|| offset .. offset + self.len())
+ }
+}
+
+impl From<NodeData> for SyntaxNode {
+ fn from(token: NodeData) -> Self {
+ Self::Leaf(token)
+ }
+}
+
+impl Debug for NodeData {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{:?}: {}", self.kind, self.len)
+ }
+}
+
+impl PartialEq for NodeData {
+ fn eq(&self, other: &Self) -> bool {
+ self.kind == other.kind && self.len == other.len
+ }
+}
diff --git a/src/parse/parser.rs b/src/syntax/parser.rs
index 3dbb7d50..83b333f4 100644
--- a/src/parse/parser.rs
+++ b/src/syntax/parser.rs
@@ -2,8 +2,7 @@ use std::fmt::{self, Display, Formatter};
use std::mem;
use std::ops::Range;
-use super::{TokenMode, Tokens};
-use crate::syntax::{ErrorPos, InnerNode, NodeData, NodeKind, SyntaxNode};
+use super::{ErrorPos, InnerNode, NodeData, NodeKind, SyntaxNode, TokenMode, Tokens};
use crate::util::EcoString;
/// A convenient token-based parser.
diff --git a/src/parse/mod.rs b/src/syntax/parsing.rs
index ac8ec6eb..10b4c4c2 100644
--- a/src/parse/mod.rs
+++ b/src/syntax/parsing.rs
@@ -1,18 +1,10 @@
-//! Parsing and tokenization.
-
-mod incremental;
-mod parser;
-mod resolve;
-mod tokens;
-
-pub use incremental::*;
-pub use parser::*;
-pub use tokens::*;
-
use std::collections::HashSet;
-use crate::syntax::ast::{Assoc, BinOp, UnOp};
-use crate::syntax::{ErrorPos, NodeKind, SyntaxNode};
+use super::ast::{Assoc, BinOp, UnOp};
+use super::{
+ ErrorPos, Group, Marker, NodeKind, ParseError, ParseResult, Parser, SyntaxNode,
+ TokenMode,
+};
use crate::util::EcoString;
/// Parse a source file.
@@ -32,7 +24,7 @@ pub fn parse_code(text: &str) -> SyntaxNode {
/// Reparse a code block.
///
/// Returns `Some` if all of the input was consumed.
-fn reparse_code_block(
+pub(crate) fn reparse_code_block(
prefix: &str,
text: &str,
end_pos: usize,
@@ -56,7 +48,7 @@ fn reparse_code_block(
/// Reparse a content block.
///
/// Returns `Some` if all of the input was consumed.
-fn reparse_content_block(
+pub(crate) fn reparse_content_block(
prefix: &str,
text: &str,
end_pos: usize,
@@ -80,7 +72,7 @@ fn reparse_content_block(
/// Reparse a sequence markup elements without the topmost node.
///
/// Returns `Some` if all of the input was consumed.
-fn reparse_markup_elements(
+pub(crate) fn reparse_markup_elements(
prefix: &str,
text: &str,
end_pos: usize,
@@ -1146,21 +1138,3 @@ fn body(p: &mut Parser) -> ParseResult {
}
}
}
-
-#[cfg(test)]
-mod tests {
- use std::fmt::Debug;
-
- #[track_caller]
- pub fn check<T>(text: &str, found: T, expected: T)
- where
- T: Debug + PartialEq,
- {
- if found != expected {
- println!("source: {text:?}");
- println!("expected: {expected:#?}");
- println!("found: {found:#?}");
- panic!("test failed");
- }
- }
-}
diff --git a/src/parse/resolve.rs b/src/syntax/resolve.rs
index 9fde0cf4..2ad35cec 100644
--- a/src/parse/resolve.rs
+++ b/src/syntax/resolve.rs
@@ -1,7 +1,6 @@
use unscanny::Scanner;
-use super::{is_ident, is_newline};
-use crate::syntax::RawKind;
+use super::{is_ident, is_newline, RawKind};
use crate::util::EcoString;
/// Resolve all escape sequences in a string.
diff --git a/src/source.rs b/src/syntax/source.rs
index 69e72d6b..1b87b1c9 100644
--- a/src/source.rs
+++ b/src/syntax/source.rs
@@ -9,8 +9,8 @@ use comemo::Prehashed;
use unscanny::Scanner;
use crate::diag::SourceResult;
-use crate::parse::{is_newline, parse, reparse};
use crate::syntax::ast::Markup;
+use crate::syntax::{is_newline, parse, reparse};
use crate::syntax::{Span, SyntaxNode};
use crate::util::{PathExt, StrExt};
diff --git a/src/syntax/span.rs b/src/syntax/span.rs
index 744aa123..d4d9a8f6 100644
--- a/src/syntax/span.rs
+++ b/src/syntax/span.rs
@@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Display, Formatter};
use std::num::NonZeroU64;
use std::ops::Range;
-use crate::syntax::SourceId;
+use super::SourceId;
/// A value with a span locating it in the source code.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
@@ -42,8 +42,8 @@ impl<T: Debug> Debug for Spanned<T> {
/// A unique identifier for a syntax node.
///
/// This is used throughout the compiler to track which source section an error
-/// or element stems from. Can be [mapped back](crate::source::Source::range)
-/// to a byte range for user facing display.
+/// or element stems from. Can be [mapped back](super::Source::range) to a byte
+/// range for user facing display.
///
/// Span ids are ordered in the tree to enable quickly finding the node with
/// some id:
diff --git a/src/parse/tokens.rs b/src/syntax/tokens.rs
index 73c64d1e..8e1b6944 100644
--- a/src/parse/tokens.rs
+++ b/src/syntax/tokens.rs
@@ -4,8 +4,8 @@ use unicode_xid::UnicodeXID;
use unscanny::Scanner;
use super::resolve::{resolve_hex, resolve_raw, resolve_string};
+use super::{ErrorPos, NodeKind, RawKind, Unit};
use crate::geom::{AngleUnit, LengthUnit};
-use crate::syntax::{ErrorPos, NodeKind, RawKind, Unit};
use crate::util::EcoString;
/// An iterator over the tokens of a string of source code.
@@ -710,8 +710,8 @@ fn is_math_id_continue(c: char) -> bool {
#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
+ use super::super::tests::check;
use super::*;
- use crate::parse::tests::check;
use ErrorPos::*;
use NodeKind::*;
diff --git a/tests/typeset.rs b/tests/typeset.rs
index 9eab55c8..235fcab3 100644
--- a/tests/typeset.rs
+++ b/tests/typeset.rs
@@ -15,15 +15,13 @@ use unscanny::Scanner;
use walkdir::WalkDir;
use typst::diag::{FileError, FileResult};
-use typst::eval::{Smart, Value};
use typst::font::{Font, FontBook};
use typst::frame::{Element, Frame};
use typst::geom::{Length, RgbaColor, Sides};
use typst::library::layout::PageNode;
use typst::library::text::{TextNode, TextSize};
-use typst::model::StyleMap;
-use typst::source::{Source, SourceId};
-use typst::syntax::SyntaxNode;
+use typst::model::{Smart, StyleMap, Value};
+use typst::syntax::{Source, SourceId, SyntaxNode};
use typst::util::{Buffer, PathExt};
use typst::{bail, Config, World};