mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Refactor error handling
This commit is contained in:
parent
757a701c1a
commit
3760748fdd
@ -1,9 +1,9 @@
|
|||||||
use std::io;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use iai::{black_box, main, Iai};
|
use iai::{black_box, main, Iai};
|
||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
|
|
||||||
|
use typst::diag::{FileError, FileResult};
|
||||||
use typst::font::{Font, FontBook};
|
use typst::font::{Font, FontBook};
|
||||||
use typst::parse::{TokenMode, Tokens};
|
use typst::parse::{TokenMode, Tokens};
|
||||||
use typst::source::{Source, SourceId};
|
use typst::source::{Source, SourceId};
|
||||||
@ -120,8 +120,8 @@ impl World for BenchWorld {
|
|||||||
&self.config
|
&self.config
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve(&self, _: &Path) -> io::Result<SourceId> {
|
fn resolve(&self, path: &Path) -> FileResult<SourceId> {
|
||||||
Err(io::ErrorKind::NotFound.into())
|
Err(FileError::NotFound(path.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source(&self, _: SourceId) -> &Source {
|
fn source(&self, _: SourceId) -> &Source {
|
||||||
@ -132,11 +132,11 @@ impl World for BenchWorld {
|
|||||||
&self.book
|
&self.book
|
||||||
}
|
}
|
||||||
|
|
||||||
fn font(&self, _: usize) -> io::Result<Font> {
|
fn font(&self, _: usize) -> Option<Font> {
|
||||||
Ok(self.font.clone())
|
Some(self.font.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file(&self, _: &Path) -> io::Result<Buffer> {
|
fn file(&self, path: &Path) -> FileResult<Buffer> {
|
||||||
Err(io::ErrorKind::NotFound.into())
|
Err(FileError::NotFound(path.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,7 +331,7 @@ fn generate_set(
|
|||||||
) -> syn::ImplItemMethod {
|
) -> syn::ImplItemMethod {
|
||||||
let user = user.map(|method| {
|
let user = user.map(|method| {
|
||||||
let block = &method.block;
|
let block = &method.block;
|
||||||
quote! { (|| -> TypResult<()> { #block; Ok(()) } )()?; }
|
quote! { (|| -> SourceResult<()> { #block; Ok(()) } )()?; }
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut shorthands = vec![];
|
let mut shorthands = vec![];
|
||||||
@ -367,7 +367,7 @@ fn generate_set(
|
|||||||
});
|
});
|
||||||
|
|
||||||
parse_quote! {
|
parse_quote! {
|
||||||
fn set(args: &mut Args, constructor: bool) -> TypResult<StyleMap> {
|
fn set(args: &mut Args, constructor: bool) -> SourceResult<StyleMap> {
|
||||||
let mut styles = StyleMap::new();
|
let mut styles = StyleMap::new();
|
||||||
#user
|
#user
|
||||||
#(#bindings)*
|
#(#bindings)*
|
||||||
|
124
src/diag.rs
124
src/diag.rs
@ -2,24 +2,28 @@
|
|||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use crate::syntax::{Span, Spanned};
|
use crate::syntax::{Span, Spanned};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// Early-return with a [`TypError`].
|
/// Early-return with a [`SourceError`].
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! bail {
|
macro_rules! bail {
|
||||||
|
($error:expr) => {
|
||||||
|
return Err(Box::new(vec![$error]))
|
||||||
|
};
|
||||||
|
|
||||||
($($tts:tt)*) => {
|
($($tts:tt)*) => {
|
||||||
return Err($crate::error!($($tts)*).into())
|
$crate::bail!($crate::error!($($tts)*))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a [`TypError`].
|
/// Construct a [`SourceError`].
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! error {
|
macro_rules! error {
|
||||||
($span:expr, $message:expr $(,)?) => {
|
($span:expr, $message:expr $(,)?) => {
|
||||||
Box::new(vec![$crate::diag::Error::new($span, $message)])
|
$crate::diag::SourceError::new($span, $message)
|
||||||
};
|
};
|
||||||
|
|
||||||
($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
|
($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
|
||||||
@ -27,18 +31,12 @@ macro_rules! error {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result type for typesetting and all its subpasses.
|
/// A result that can carry multiple source errors.
|
||||||
pub type TypResult<T> = Result<T, TypError>;
|
pub type SourceResult<T> = Result<T, Box<Vec<SourceError>>>;
|
||||||
|
|
||||||
/// The error type for typesetting and all its subpasses.
|
|
||||||
pub type TypError = Box<Vec<Error>>;
|
|
||||||
|
|
||||||
/// A result type with a string error message.
|
|
||||||
pub type StrResult<T> = Result<T, String>;
|
|
||||||
|
|
||||||
/// An error in a source file.
|
/// An error in a source file.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Error {
|
pub struct SourceError {
|
||||||
/// The erroneous node in the source code.
|
/// The erroneous node in the source code.
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
/// A diagnostic message describing the problem.
|
/// A diagnostic message describing the problem.
|
||||||
@ -47,7 +45,7 @@ pub struct Error {
|
|||||||
pub trace: Vec<Spanned<Tracepoint>>,
|
pub trace: Vec<Spanned<Tracepoint>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl SourceError {
|
||||||
/// Create a new, bare error.
|
/// Create a new, bare error.
|
||||||
pub fn new(span: Span, message: impl Into<String>) -> Self {
|
pub fn new(span: Span, message: impl Into<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -58,7 +56,7 @@ impl Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A part of an error's [trace](Error::trace).
|
/// A part of an error's [trace](SourceError::trace).
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum Tracepoint {
|
pub enum Tracepoint {
|
||||||
/// A function call.
|
/// A function call.
|
||||||
@ -83,22 +81,7 @@ impl Display for Tracepoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a [`StrResult`] to a [`TypResult`] by adding span information.
|
/// Enrich a [`SourceResult`] with a tracepoint.
|
||||||
pub trait At<T> {
|
|
||||||
/// Add the span information.
|
|
||||||
fn at(self, span: Span) -> TypResult<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, S> At<T> for Result<T, S>
|
|
||||||
where
|
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
fn at(self, span: Span) -> TypResult<T> {
|
|
||||||
self.map_err(|message| error!(span, message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enrich a [`TypResult`] with a tracepoint.
|
|
||||||
pub trait Trace<T> {
|
pub trait Trace<T> {
|
||||||
/// Add the tracepoint to all errors that lie outside the `span`.
|
/// Add the tracepoint to all errors that lie outside the `span`.
|
||||||
fn trace<F>(self, world: &dyn World, make_point: F, span: Span) -> Self
|
fn trace<F>(self, world: &dyn World, make_point: F, span: Span) -> Self
|
||||||
@ -106,7 +89,7 @@ pub trait Trace<T> {
|
|||||||
F: Fn() -> Tracepoint;
|
F: Fn() -> Tracepoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Trace<T> for TypResult<T> {
|
impl<T> Trace<T> for SourceResult<T> {
|
||||||
fn trace<F>(self, world: &dyn World, make_point: F, span: Span) -> Self
|
fn trace<F>(self, world: &dyn World, make_point: F, span: Span) -> Self
|
||||||
where
|
where
|
||||||
F: Fn() -> Tracepoint,
|
F: Fn() -> Tracepoint,
|
||||||
@ -127,6 +110,9 @@ impl<T> Trace<T> for TypResult<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A result type with a string error message.
|
||||||
|
pub type StrResult<T> = Result<T, String>;
|
||||||
|
|
||||||
/// Transform `expected X, found Y` into `expected X or A, found Y`.
|
/// Transform `expected X, found Y` into `expected X or A, found Y`.
|
||||||
pub fn with_alternative(msg: String, alt: &str) -> String {
|
pub fn with_alternative(msg: String, alt: &str) -> String {
|
||||||
let mut parts = msg.split(", found ");
|
let mut parts = msg.split(", found ");
|
||||||
@ -137,12 +123,70 @@ pub fn with_alternative(msg: String, alt: &str) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format a file loading failure.
|
/// Convert a [`StrResult`] to a [`SourceResult`] by adding span information.
|
||||||
pub fn failed_to_load(target: &str, path: &Path, error: io::Error) -> String {
|
pub trait At<T> {
|
||||||
match error.kind() {
|
/// Add the span information.
|
||||||
io::ErrorKind::NotFound => {
|
fn at(self, span: Span) -> SourceResult<T>;
|
||||||
format!("file not found (searched at {})", path.display())
|
}
|
||||||
}
|
|
||||||
_ => format!("failed to load {target} ({error})"),
|
impl<T, S> At<T> for Result<T, S>
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
fn at(self, span: Span) -> SourceResult<T> {
|
||||||
|
self.map_err(|message| Box::new(vec![error!(span, message)]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A result type with a file-related error.
|
||||||
|
pub type FileResult<T> = Result<T, FileError>;
|
||||||
|
|
||||||
|
/// An error that occured while trying to load of a file.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum FileError {
|
||||||
|
/// A file was not found at this path.
|
||||||
|
NotFound(PathBuf),
|
||||||
|
/// A file could not be accessed.
|
||||||
|
AccessDenied,
|
||||||
|
/// The file was not valid UTF-8, but should have been.
|
||||||
|
InvalidUtf8,
|
||||||
|
/// Another error.
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileError {
|
||||||
|
/// Create a file error from an I/O error.
|
||||||
|
pub fn from_io(error: io::Error, path: &Path) -> Self {
|
||||||
|
match error.kind() {
|
||||||
|
io::ErrorKind::NotFound => Self::NotFound(path.into()),
|
||||||
|
io::ErrorKind::PermissionDenied => Self::AccessDenied,
|
||||||
|
io::ErrorKind::InvalidData
|
||||||
|
if error.to_string().contains("stream did not contain valid UTF-8") =>
|
||||||
|
{
|
||||||
|
Self::InvalidUtf8
|
||||||
|
}
|
||||||
|
_ => Self::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for FileError {}
|
||||||
|
|
||||||
|
impl Display for FileError {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::NotFound(path) => {
|
||||||
|
write!(f, "file not found (searched at {})", path.display())
|
||||||
|
}
|
||||||
|
Self::AccessDenied => f.pad("file access denied"),
|
||||||
|
Self::InvalidUtf8 => f.pad("file is not valid utf-8"),
|
||||||
|
Self::Other => f.pad("failed to load file"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FileError> for String {
|
||||||
|
fn from(error: FileError) -> Self {
|
||||||
|
error.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::fmt::{self, Debug, Formatter, Write};
|
use std::fmt::{self, Debug, Formatter, Write};
|
||||||
|
|
||||||
use super::{Array, Cast, Dict, Str, Value};
|
use super::{Array, Cast, Dict, Str, Value};
|
||||||
use crate::diag::{At, TypResult};
|
use crate::diag::{At, SourceResult};
|
||||||
use crate::syntax::{Span, Spanned};
|
use crate::syntax::{Span, Spanned};
|
||||||
|
|
||||||
/// Evaluated arguments to a function.
|
/// Evaluated arguments to a function.
|
||||||
@ -48,7 +48,7 @@ impl Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Consume and cast the first positional argument if there is one.
|
/// Consume and cast the first positional argument if there is one.
|
||||||
pub fn eat<T>(&mut self) -> TypResult<Option<T>>
|
pub fn eat<T>(&mut self) -> SourceResult<Option<T>>
|
||||||
where
|
where
|
||||||
T: Cast<Spanned<Value>>,
|
T: Cast<Spanned<Value>>,
|
||||||
{
|
{
|
||||||
@ -66,7 +66,7 @@ impl Args {
|
|||||||
///
|
///
|
||||||
/// Returns a `missing argument: {what}` error if no positional argument is
|
/// Returns a `missing argument: {what}` error if no positional argument is
|
||||||
/// left.
|
/// left.
|
||||||
pub fn expect<T>(&mut self, what: &str) -> TypResult<T>
|
pub fn expect<T>(&mut self, what: &str) -> SourceResult<T>
|
||||||
where
|
where
|
||||||
T: Cast<Spanned<Value>>,
|
T: Cast<Spanned<Value>>,
|
||||||
{
|
{
|
||||||
@ -77,7 +77,7 @@ impl Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find and consume the first castable positional argument.
|
/// Find and consume the first castable positional argument.
|
||||||
pub fn find<T>(&mut self) -> TypResult<Option<T>>
|
pub fn find<T>(&mut self) -> SourceResult<Option<T>>
|
||||||
where
|
where
|
||||||
T: Cast<Spanned<Value>>,
|
T: Cast<Spanned<Value>>,
|
||||||
{
|
{
|
||||||
@ -92,7 +92,7 @@ impl Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find and consume all castable positional arguments.
|
/// Find and consume all castable positional arguments.
|
||||||
pub fn all<T>(&mut self) -> TypResult<Vec<T>>
|
pub fn all<T>(&mut self) -> SourceResult<Vec<T>>
|
||||||
where
|
where
|
||||||
T: Cast<Spanned<Value>>,
|
T: Cast<Spanned<Value>>,
|
||||||
{
|
{
|
||||||
@ -105,7 +105,7 @@ impl Args {
|
|||||||
|
|
||||||
/// Cast and remove the value for the given named argument, returning an
|
/// Cast and remove the value for the given named argument, returning an
|
||||||
/// error if the conversion fails.
|
/// error if the conversion fails.
|
||||||
pub fn named<T>(&mut self, name: &str) -> TypResult<Option<T>>
|
pub fn named<T>(&mut self, name: &str) -> SourceResult<Option<T>>
|
||||||
where
|
where
|
||||||
T: Cast<Spanned<Value>>,
|
T: Cast<Spanned<Value>>,
|
||||||
{
|
{
|
||||||
@ -126,7 +126,7 @@ impl Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Same as named, but with fallback to find.
|
/// Same as named, but with fallback to find.
|
||||||
pub fn named_or_find<T>(&mut self, name: &str) -> TypResult<Option<T>>
|
pub fn named_or_find<T>(&mut self, name: &str) -> SourceResult<Option<T>>
|
||||||
where
|
where
|
||||||
T: Cast<Spanned<Value>>,
|
T: Cast<Spanned<Value>>,
|
||||||
{
|
{
|
||||||
@ -146,7 +146,7 @@ impl Args {
|
|||||||
|
|
||||||
/// Return an "unexpected argument" error if there is any remaining
|
/// Return an "unexpected argument" error if there is any remaining
|
||||||
/// argument.
|
/// argument.
|
||||||
pub fn finish(self) -> TypResult<()> {
|
pub fn finish(self) -> SourceResult<()> {
|
||||||
if let Some(arg) = self.items.first() {
|
if let Some(arg) = self.items.first() {
|
||||||
bail!(arg.span, "unexpected argument");
|
bail!(arg.span, "unexpected argument");
|
||||||
}
|
}
|
||||||
@ -171,17 +171,17 @@ impl Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Reinterpret these arguments as actually being an array index.
|
/// Reinterpret these arguments as actually being an array index.
|
||||||
pub fn into_index(self) -> TypResult<i64> {
|
pub fn into_index(self) -> SourceResult<i64> {
|
||||||
self.into_castable("index")
|
self.into_castable("index")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reinterpret these arguments as actually being a dictionary key.
|
/// Reinterpret these arguments as actually being a dictionary key.
|
||||||
pub fn into_key(self) -> TypResult<Str> {
|
pub fn into_key(self) -> SourceResult<Str> {
|
||||||
self.into_castable("key")
|
self.into_castable("key")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reinterpret these arguments as actually being a single castable thing.
|
/// Reinterpret these arguments as actually being a single castable thing.
|
||||||
fn into_castable<T: Cast>(self, what: &str) -> TypResult<T> {
|
fn into_castable<T: Cast>(self, what: &str) -> SourceResult<T> {
|
||||||
let mut iter = self.items.into_iter();
|
let mut iter = self.items.into_iter();
|
||||||
let value = match iter.next() {
|
let value = match iter.next() {
|
||||||
Some(Arg { name: None, value, .. }) => value.v.cast().at(value.span)?,
|
Some(Arg { name: None, value, .. }) => value.v.cast().at(value.span)?,
|
||||||
|
@ -4,7 +4,7 @@ use std::ops::{Add, AddAssign};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{ops, Args, Func, Value, Vm};
|
use super::{ops, Args, Func, Value, Vm};
|
||||||
use crate::diag::{At, StrResult, TypResult};
|
use crate::diag::{At, SourceResult, StrResult};
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::Spanned;
|
||||||
use crate::util::ArcExt;
|
use crate::util::ArcExt;
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ impl Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the first matching element.
|
/// Return the first matching element.
|
||||||
pub fn find(&self, vm: &mut Vm, f: Spanned<Func>) -> TypResult<Option<Value>> {
|
pub fn find(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Option<Value>> {
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
let args = Args::new(f.span, [item.clone()]);
|
let args = Args::new(f.span, [item.clone()]);
|
||||||
if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
|
if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
|
||||||
@ -136,7 +136,7 @@ impl Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the index of the first matching element.
|
/// Return the index of the first matching element.
|
||||||
pub fn position(&self, vm: &mut Vm, f: Spanned<Func>) -> TypResult<Option<i64>> {
|
pub fn position(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Option<i64>> {
|
||||||
for (i, item) in self.iter().enumerate() {
|
for (i, item) in self.iter().enumerate() {
|
||||||
let args = Args::new(f.span, [item.clone()]);
|
let args = Args::new(f.span, [item.clone()]);
|
||||||
if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
|
if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
|
||||||
@ -149,7 +149,7 @@ impl Array {
|
|||||||
|
|
||||||
/// Return a new array with only those elements for which the function
|
/// Return a new array with only those elements for which the function
|
||||||
/// returns true.
|
/// returns true.
|
||||||
pub fn filter(&self, vm: &mut Vm, f: Spanned<Func>) -> TypResult<Self> {
|
pub fn filter(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Self> {
|
||||||
let mut kept = vec![];
|
let mut kept = vec![];
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
let args = Args::new(f.span, [item.clone()]);
|
let args = Args::new(f.span, [item.clone()]);
|
||||||
@ -161,10 +161,9 @@ impl Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Transform each item in the array with a function.
|
/// Transform each item in the array with a function.
|
||||||
pub fn map(&self, vm: &mut Vm, f: Spanned<Func>) -> TypResult<Self> {
|
pub fn map(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Self> {
|
||||||
let enumerate = f.v.argc() == Some(2);
|
let enumerate = f.v.argc() == Some(2);
|
||||||
Ok(self
|
self.iter()
|
||||||
.iter()
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, item)| {
|
.map(|(i, item)| {
|
||||||
let mut args = Args::new(f.span, []);
|
let mut args = Args::new(f.span, []);
|
||||||
@ -174,11 +173,11 @@ impl Array {
|
|||||||
args.push(f.span, item.clone());
|
args.push(f.span, item.clone());
|
||||||
f.v.call(vm, args)
|
f.v.call(vm, args)
|
||||||
})
|
})
|
||||||
.collect::<TypResult<_>>()?)
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether any element matches.
|
/// Whether any element matches.
|
||||||
pub fn any(&self, vm: &mut Vm, f: Spanned<Func>) -> TypResult<bool> {
|
pub fn any(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<bool> {
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
let args = Args::new(f.span, [item.clone()]);
|
let args = Args::new(f.span, [item.clone()]);
|
||||||
if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
|
if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
|
||||||
@ -190,7 +189,7 @@ impl Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Whether all elements match.
|
/// Whether all elements match.
|
||||||
pub fn all(&self, vm: &mut Vm, f: Spanned<Func>) -> TypResult<bool> {
|
pub fn all(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<bool> {
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
let args = Args::new(f.span, [item.clone()]);
|
let args = Args::new(f.span, [item.clone()]);
|
||||||
if !f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
|
if !f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
|
||||||
|
@ -4,7 +4,7 @@ use std::ops::{Add, AddAssign};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{Args, Array, Func, Str, Value, Vm};
|
use super::{Args, Array, Func, Str, Value, Vm};
|
||||||
use crate::diag::{StrResult, TypResult};
|
use crate::diag::{SourceResult, StrResult};
|
||||||
use crate::parse::is_ident;
|
use crate::parse::is_ident;
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::Spanned;
|
||||||
use crate::util::ArcExt;
|
use crate::util::ArcExt;
|
||||||
@ -101,14 +101,13 @@ impl Dict {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Transform each pair in the array with a function.
|
/// Transform each pair in the array with a function.
|
||||||
pub fn map(&self, vm: &mut Vm, f: Spanned<Func>) -> TypResult<Array> {
|
pub fn map(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Array> {
|
||||||
Ok(self
|
self.iter()
|
||||||
.iter()
|
|
||||||
.map(|(key, value)| {
|
.map(|(key, value)| {
|
||||||
let args = Args::new(f.span, [Value::Str(key.clone()), value.clone()]);
|
let args = Args::new(f.span, [Value::Str(key.clone()), value.clone()]);
|
||||||
f.v.call(vm, args)
|
f.v.call(vm, args)
|
||||||
})
|
})
|
||||||
.collect::<TypResult<_>>()?)
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over pairs of references to the contained keys and values.
|
/// Iterate over pairs of references to the contained keys and values.
|
||||||
|
@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{Args, Eval, Flow, Scope, Scopes, Value, Vm};
|
use super::{Args, Eval, Flow, Scope, Scopes, Value, Vm};
|
||||||
use crate::diag::{StrResult, TypResult};
|
use crate::diag::{SourceResult, StrResult};
|
||||||
use crate::model::{Content, NodeId, StyleMap};
|
use crate::model::{Content, NodeId, StyleMap};
|
||||||
use crate::source::SourceId;
|
use crate::source::SourceId;
|
||||||
use crate::syntax::ast::Expr;
|
use crate::syntax::ast::Expr;
|
||||||
@ -29,7 +29,7 @@ impl Func {
|
|||||||
/// Create a new function from a native rust function.
|
/// Create a new function from a native rust function.
|
||||||
pub fn from_fn(
|
pub fn from_fn(
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
func: fn(&mut Vm, &mut Args) -> TypResult<Value>,
|
func: fn(&mut Vm, &mut Args) -> SourceResult<Value>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self(Arc::new(Repr::Native(Native {
|
Self(Arc::new(Repr::Native(Native {
|
||||||
name,
|
name,
|
||||||
@ -86,7 +86,7 @@ impl Func {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call the function with the given arguments.
|
/// Call the function with the given arguments.
|
||||||
pub fn call(&self, vm: &mut Vm, mut args: Args) -> TypResult<Value> {
|
pub fn call(&self, vm: &mut Vm, mut args: Args) -> SourceResult<Value> {
|
||||||
let value = match self.0.as_ref() {
|
let value = match self.0.as_ref() {
|
||||||
Repr::Native(native) => (native.func)(vm, &mut args)?,
|
Repr::Native(native) => (native.func)(vm, &mut args)?,
|
||||||
Repr::Closure(closure) => closure.call(vm, &mut args)?,
|
Repr::Closure(closure) => closure.call(vm, &mut args)?,
|
||||||
@ -100,13 +100,13 @@ impl Func {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call the function without an existing virtual machine.
|
/// Call the function without an existing virtual machine.
|
||||||
pub fn call_detached(&self, world: &dyn World, args: Args) -> TypResult<Value> {
|
pub fn call_detached(&self, world: &dyn World, args: Args) -> SourceResult<Value> {
|
||||||
let mut vm = Vm::new(world, vec![], Scopes::new(None));
|
let mut vm = Vm::new(world, vec![], Scopes::new(None));
|
||||||
self.call(&mut vm, args)
|
self.call(&mut vm, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the function's set rule and return the resulting style map.
|
/// Execute the function's set rule and return the resulting style map.
|
||||||
pub fn set(&self, mut args: Args) -> TypResult<StyleMap> {
|
pub fn set(&self, mut args: Args) -> SourceResult<StyleMap> {
|
||||||
let styles = match self.0.as_ref() {
|
let styles = match self.0.as_ref() {
|
||||||
Repr::Native(Native { set: Some(set), .. }) => set(&mut args)?,
|
Repr::Native(Native { set: Some(set), .. }) => set(&mut args)?,
|
||||||
_ => StyleMap::new(),
|
_ => StyleMap::new(),
|
||||||
@ -144,9 +144,9 @@ struct Native {
|
|||||||
/// The name of the function.
|
/// The name of the function.
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
/// The function pointer.
|
/// The function pointer.
|
||||||
pub func: fn(&mut Vm, &mut Args) -> TypResult<Value>,
|
pub func: fn(&mut Vm, &mut Args) -> SourceResult<Value>,
|
||||||
/// The set rule.
|
/// The set rule.
|
||||||
pub set: Option<fn(&mut Args) -> TypResult<StyleMap>>,
|
pub set: Option<fn(&mut Args) -> SourceResult<StyleMap>>,
|
||||||
/// The id of the node to customize with this function's show rule.
|
/// The id of the node to customize with this function's show rule.
|
||||||
pub node: Option<NodeId>,
|
pub node: Option<NodeId>,
|
||||||
}
|
}
|
||||||
@ -169,13 +169,13 @@ pub trait Node: 'static {
|
|||||||
///
|
///
|
||||||
/// This is passed only the arguments that remain after execution of the
|
/// This is passed only the arguments that remain after execution of the
|
||||||
/// node's set rule.
|
/// node's set rule.
|
||||||
fn construct(vm: &mut Vm, args: &mut Args) -> TypResult<Content>;
|
fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content>;
|
||||||
|
|
||||||
/// Parse relevant arguments into style properties for this node.
|
/// Parse relevant arguments into style properties for this node.
|
||||||
///
|
///
|
||||||
/// When `constructor` is true, [`construct`](Self::construct) will run
|
/// When `constructor` is true, [`construct`](Self::construct) will run
|
||||||
/// after this invocation of `set` with the remaining arguments.
|
/// after this invocation of `set` with the remaining arguments.
|
||||||
fn set(args: &mut Args, constructor: bool) -> TypResult<StyleMap>;
|
fn set(args: &mut Args, constructor: bool) -> SourceResult<StyleMap>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A user-defined closure.
|
/// A user-defined closure.
|
||||||
@ -198,7 +198,7 @@ pub struct Closure {
|
|||||||
|
|
||||||
impl Closure {
|
impl Closure {
|
||||||
/// Call the function in the context with the arguments.
|
/// Call the function in the context with the arguments.
|
||||||
pub fn call(&self, vm: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn call(&self, vm: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
// Don't leak the scopes from the call site. Instead, we use the scope
|
// Don't leak the scopes from the call site. Instead, we use the scope
|
||||||
// of captured variables we collected earlier.
|
// of captured variables we collected earlier.
|
||||||
let mut scopes = Scopes::new(None);
|
let mut scopes = Scopes::new(None);
|
||||||
@ -235,7 +235,7 @@ impl Closure {
|
|||||||
match sub.flow {
|
match sub.flow {
|
||||||
Some(Flow::Return(_, Some(explicit))) => return Ok(explicit),
|
Some(Flow::Return(_, Some(explicit))) => return Ok(explicit),
|
||||||
Some(Flow::Return(_, None)) => {}
|
Some(Flow::Return(_, None)) => {}
|
||||||
Some(flow) => return Err(flow.forbidden())?,
|
Some(flow) => bail!(flow.forbidden()),
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Methods on values.
|
//! Methods on values.
|
||||||
|
|
||||||
use super::{Args, Value, Vm};
|
use super::{Args, Value, Vm};
|
||||||
use crate::diag::{At, TypResult};
|
use crate::diag::{At, SourceResult};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ pub fn call(
|
|||||||
method: &str,
|
method: &str,
|
||||||
mut args: Args,
|
mut args: Args,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> TypResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let name = value.type_name();
|
let name = value.type_name();
|
||||||
let missing = || Err(missing_method(name, method)).at(span);
|
let missing = || Err(missing_method(name, method)).at(span);
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ pub fn call_mut(
|
|||||||
method: &str,
|
method: &str,
|
||||||
mut args: Args,
|
mut args: Args,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> TypResult<()> {
|
) -> SourceResult<()> {
|
||||||
let name = value.type_name();
|
let name = value.type_name();
|
||||||
let missing = || Err(missing_method(name, method)).at(span);
|
let missing = || Err(missing_method(name, method)).at(span);
|
||||||
|
|
||||||
|
110
src/eval/mod.rs
110
src/eval/mod.rs
@ -36,7 +36,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use crate::diag::{failed_to_load, At, StrResult, Trace, Tracepoint, TypResult};
|
use crate::diag::{At, SourceResult, StrResult, Trace, Tracepoint};
|
||||||
use crate::geom::{Angle, Em, Fraction, Length, Ratio};
|
use crate::geom::{Angle, Em, Fraction, Length, Ratio};
|
||||||
use crate::library;
|
use crate::library;
|
||||||
use crate::model::{Content, Pattern, Recipe, StyleEntry, StyleMap};
|
use crate::model::{Content, Pattern, Recipe, StyleEntry, StyleMap};
|
||||||
@ -55,7 +55,7 @@ pub fn evaluate(
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
id: SourceId,
|
id: SourceId,
|
||||||
mut route: Vec<SourceId>,
|
mut route: Vec<SourceId>,
|
||||||
) -> TypResult<Module> {
|
) -> SourceResult<Module> {
|
||||||
// Prevent cyclic evaluation.
|
// Prevent cyclic evaluation.
|
||||||
if route.contains(&id) {
|
if route.contains(&id) {
|
||||||
let path = world.source(id).path().display();
|
let path = world.source(id).path().display();
|
||||||
@ -73,7 +73,7 @@ pub fn evaluate(
|
|||||||
|
|
||||||
// Handle control flow.
|
// Handle control flow.
|
||||||
if let Some(flow) = vm.flow {
|
if let Some(flow) = vm.flow {
|
||||||
return Err(flow.forbidden());
|
bail!(flow.forbidden());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble the module.
|
// Assemble the module.
|
||||||
@ -95,13 +95,13 @@ pub trait Eval {
|
|||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
/// Evaluate the expression to the output value.
|
/// Evaluate the expression to the output value.
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output>;
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Markup {
|
impl Eval for Markup {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
eval_markup(vm, &mut self.nodes())
|
eval_markup(vm, &mut self.nodes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ impl Eval for Markup {
|
|||||||
fn eval_markup(
|
fn eval_markup(
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
nodes: &mut impl Iterator<Item = MarkupNode>,
|
nodes: &mut impl Iterator<Item = MarkupNode>,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
let flow = vm.flow.take();
|
let flow = vm.flow.take();
|
||||||
let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
|
let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ fn eval_markup(
|
|||||||
impl Eval for MarkupNode {
|
impl Eval for MarkupNode {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Space => Content::Space,
|
Self::Space => Content::Space,
|
||||||
Self::Parbreak => Content::Parbreak,
|
Self::Parbreak => Content::Parbreak,
|
||||||
@ -181,7 +181,7 @@ impl Eval for MarkupNode {
|
|||||||
impl Eval for StrongNode {
|
impl Eval for StrongNode {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(Content::show(library::text::StrongNode(
|
Ok(Content::show(library::text::StrongNode(
|
||||||
self.body().eval(vm)?,
|
self.body().eval(vm)?,
|
||||||
)))
|
)))
|
||||||
@ -191,7 +191,7 @@ impl Eval for StrongNode {
|
|||||||
impl Eval for EmphNode {
|
impl Eval for EmphNode {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(Content::show(library::text::EmphNode(
|
Ok(Content::show(library::text::EmphNode(
|
||||||
self.body().eval(vm)?,
|
self.body().eval(vm)?,
|
||||||
)))
|
)))
|
||||||
@ -201,7 +201,7 @@ impl Eval for EmphNode {
|
|||||||
impl Eval for RawNode {
|
impl Eval for RawNode {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let content = Content::show(library::text::RawNode {
|
let content = Content::show(library::text::RawNode {
|
||||||
text: self.text.clone(),
|
text: self.text.clone(),
|
||||||
block: self.block,
|
block: self.block,
|
||||||
@ -216,7 +216,7 @@ impl Eval for RawNode {
|
|||||||
impl Eval for Spanned<MathNode> {
|
impl Eval for Spanned<MathNode> {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(Content::show(library::math::MathNode {
|
Ok(Content::show(library::math::MathNode {
|
||||||
formula: self.clone().map(|math| math.formula),
|
formula: self.clone().map(|math| math.formula),
|
||||||
display: self.v.display,
|
display: self.v.display,
|
||||||
@ -227,7 +227,7 @@ impl Eval for Spanned<MathNode> {
|
|||||||
impl Eval for HeadingNode {
|
impl Eval for HeadingNode {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(Content::show(library::structure::HeadingNode {
|
Ok(Content::show(library::structure::HeadingNode {
|
||||||
body: self.body().eval(vm)?,
|
body: self.body().eval(vm)?,
|
||||||
level: self.level(),
|
level: self.level(),
|
||||||
@ -238,7 +238,7 @@ impl Eval for HeadingNode {
|
|||||||
impl Eval for ListNode {
|
impl Eval for ListNode {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(Content::Item(library::structure::ListItem {
|
Ok(Content::Item(library::structure::ListItem {
|
||||||
kind: library::structure::UNORDERED,
|
kind: library::structure::UNORDERED,
|
||||||
number: None,
|
number: None,
|
||||||
@ -250,7 +250,7 @@ impl Eval for ListNode {
|
|||||||
impl Eval for EnumNode {
|
impl Eval for EnumNode {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(Content::Item(library::structure::ListItem {
|
Ok(Content::Item(library::structure::ListItem {
|
||||||
kind: library::structure::ORDERED,
|
kind: library::structure::ORDERED,
|
||||||
number: self.number(),
|
number: self.number(),
|
||||||
@ -262,7 +262,7 @@ impl Eval for EnumNode {
|
|||||||
impl Eval for Expr {
|
impl Eval for Expr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let forbidden = |name| {
|
let forbidden = |name| {
|
||||||
error!(
|
error!(
|
||||||
self.span(),
|
self.span(),
|
||||||
@ -285,9 +285,9 @@ impl Eval for Expr {
|
|||||||
Self::Unary(v) => v.eval(vm),
|
Self::Unary(v) => v.eval(vm),
|
||||||
Self::Binary(v) => v.eval(vm),
|
Self::Binary(v) => v.eval(vm),
|
||||||
Self::Let(v) => v.eval(vm),
|
Self::Let(v) => v.eval(vm),
|
||||||
Self::Set(_) => Err(forbidden("set")),
|
Self::Set(_) => bail!(forbidden("set")),
|
||||||
Self::Show(_) => Err(forbidden("show")),
|
Self::Show(_) => bail!(forbidden("show")),
|
||||||
Self::Wrap(_) => Err(forbidden("wrap")),
|
Self::Wrap(_) => bail!(forbidden("wrap")),
|
||||||
Self::If(v) => v.eval(vm),
|
Self::If(v) => v.eval(vm),
|
||||||
Self::While(v) => v.eval(vm),
|
Self::While(v) => v.eval(vm),
|
||||||
Self::For(v) => v.eval(vm),
|
Self::For(v) => v.eval(vm),
|
||||||
@ -303,7 +303,7 @@ impl Eval for Expr {
|
|||||||
impl Eval for Lit {
|
impl Eval for Lit {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(match self.kind() {
|
Ok(match self.kind() {
|
||||||
LitKind::None => Value::None,
|
LitKind::None => Value::None,
|
||||||
LitKind::Auto => Value::Auto,
|
LitKind::Auto => Value::Auto,
|
||||||
@ -325,7 +325,7 @@ impl Eval for Lit {
|
|||||||
impl Eval for Ident {
|
impl Eval for Ident {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
vm.scopes.get(self).cloned().at(self.span())
|
vm.scopes.get(self).cloned().at(self.span())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -333,7 +333,7 @@ impl Eval for Ident {
|
|||||||
impl Eval for CodeBlock {
|
impl Eval for CodeBlock {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
vm.scopes.enter();
|
vm.scopes.enter();
|
||||||
let output = eval_code(vm, &mut self.exprs())?;
|
let output = eval_code(vm, &mut self.exprs())?;
|
||||||
vm.scopes.exit();
|
vm.scopes.exit();
|
||||||
@ -342,7 +342,7 @@ impl Eval for CodeBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a stream of expressions.
|
/// Evaluate a stream of expressions.
|
||||||
fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> TypResult<Value> {
|
fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> SourceResult<Value> {
|
||||||
let flow = vm.flow.take();
|
let flow = vm.flow.take();
|
||||||
let mut output = Value::None;
|
let mut output = Value::None;
|
||||||
|
|
||||||
@ -394,7 +394,7 @@ fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> TypResult<V
|
|||||||
impl Eval for ContentBlock {
|
impl Eval for ContentBlock {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
vm.scopes.enter();
|
vm.scopes.enter();
|
||||||
let content = self.body().eval(vm)?;
|
let content = self.body().eval(vm)?;
|
||||||
vm.scopes.exit();
|
vm.scopes.exit();
|
||||||
@ -405,7 +405,7 @@ impl Eval for ContentBlock {
|
|||||||
impl Eval for GroupExpr {
|
impl Eval for GroupExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
self.expr().eval(vm)
|
self.expr().eval(vm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,7 +413,7 @@ impl Eval for GroupExpr {
|
|||||||
impl Eval for ArrayExpr {
|
impl Eval for ArrayExpr {
|
||||||
type Output = Array;
|
type Output = Array;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let items = self.items();
|
let items = self.items();
|
||||||
|
|
||||||
let mut vec = Vec::with_capacity(items.size_hint().0);
|
let mut vec = Vec::with_capacity(items.size_hint().0);
|
||||||
@ -435,7 +435,7 @@ impl Eval for ArrayExpr {
|
|||||||
impl Eval for DictExpr {
|
impl Eval for DictExpr {
|
||||||
type Output = Dict;
|
type Output = Dict;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
|
|
||||||
for item in self.items() {
|
for item in self.items() {
|
||||||
@ -465,7 +465,7 @@ impl Eval for DictExpr {
|
|||||||
impl Eval for UnaryExpr {
|
impl Eval for UnaryExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let value = self.expr().eval(vm)?;
|
let value = self.expr().eval(vm)?;
|
||||||
let result = match self.op() {
|
let result = match self.op() {
|
||||||
UnOp::Pos => ops::pos(value),
|
UnOp::Pos => ops::pos(value),
|
||||||
@ -479,7 +479,7 @@ impl Eval for UnaryExpr {
|
|||||||
impl Eval for BinaryExpr {
|
impl Eval for BinaryExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
match self.op() {
|
match self.op() {
|
||||||
BinOp::Add => self.apply(vm, ops::add),
|
BinOp::Add => self.apply(vm, ops::add),
|
||||||
BinOp::Sub => self.apply(vm, ops::sub),
|
BinOp::Sub => self.apply(vm, ops::sub),
|
||||||
@ -510,7 +510,7 @@ impl BinaryExpr {
|
|||||||
&self,
|
&self,
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
op: fn(Value, Value) -> StrResult<Value>,
|
op: fn(Value, Value) -> StrResult<Value>,
|
||||||
) -> TypResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let lhs = self.lhs().eval(vm)?;
|
let lhs = self.lhs().eval(vm)?;
|
||||||
|
|
||||||
// Short-circuit boolean operations.
|
// Short-circuit boolean operations.
|
||||||
@ -529,7 +529,7 @@ impl BinaryExpr {
|
|||||||
&self,
|
&self,
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
op: fn(Value, Value) -> StrResult<Value>,
|
op: fn(Value, Value) -> StrResult<Value>,
|
||||||
) -> TypResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let rhs = self.rhs().eval(vm)?;
|
let rhs = self.rhs().eval(vm)?;
|
||||||
let location = self.lhs().access(vm)?;
|
let location = self.lhs().access(vm)?;
|
||||||
let lhs = std::mem::take(&mut *location);
|
let lhs = std::mem::take(&mut *location);
|
||||||
@ -541,7 +541,7 @@ impl BinaryExpr {
|
|||||||
impl Eval for FieldAccess {
|
impl Eval for FieldAccess {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let object = self.object().eval(vm)?;
|
let object = self.object().eval(vm)?;
|
||||||
let span = self.field().span();
|
let span = self.field().span();
|
||||||
let field = self.field().take();
|
let field = self.field().take();
|
||||||
@ -567,7 +567,7 @@ impl Eval for FieldAccess {
|
|||||||
impl Eval for FuncCall {
|
impl Eval for FuncCall {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let callee = self.callee().eval(vm)?;
|
let callee = self.callee().eval(vm)?;
|
||||||
let args = self.args().eval(vm)?;
|
let args = self.args().eval(vm)?;
|
||||||
|
|
||||||
@ -591,7 +591,7 @@ impl Eval for FuncCall {
|
|||||||
impl Eval for MethodCall {
|
impl Eval for MethodCall {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let span = self.span();
|
let span = self.span();
|
||||||
let method = self.method();
|
let method = self.method();
|
||||||
let point = || Tracepoint::Call(Some(method.to_string()));
|
let point = || Tracepoint::Call(Some(method.to_string()));
|
||||||
@ -613,7 +613,7 @@ impl Eval for MethodCall {
|
|||||||
impl Eval for CallArgs {
|
impl Eval for CallArgs {
|
||||||
type Output = Args;
|
type Output = Args;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
|
|
||||||
for arg in self.items() {
|
for arg in self.items() {
|
||||||
@ -662,7 +662,7 @@ impl Eval for CallArgs {
|
|||||||
impl Eval for ClosureExpr {
|
impl Eval for ClosureExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
// The closure's name is defined by its let binding if there's one.
|
// The closure's name is defined by its let binding if there's one.
|
||||||
let name = self.name().map(Ident::take);
|
let name = self.name().map(Ident::take);
|
||||||
|
|
||||||
@ -709,7 +709,7 @@ impl Eval for ClosureExpr {
|
|||||||
impl Eval for LetExpr {
|
impl Eval for LetExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let value = match self.init() {
|
let value = match self.init() {
|
||||||
Some(expr) => expr.eval(vm)?,
|
Some(expr) => expr.eval(vm)?,
|
||||||
None => Value::None,
|
None => Value::None,
|
||||||
@ -722,7 +722,7 @@ impl Eval for LetExpr {
|
|||||||
impl Eval for SetExpr {
|
impl Eval for SetExpr {
|
||||||
type Output = StyleMap;
|
type Output = StyleMap;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let target = self.target();
|
let target = self.target();
|
||||||
let target = target.eval(vm)?.cast::<Func>().at(target.span())?;
|
let target = target.eval(vm)?.cast::<Func>().at(target.span())?;
|
||||||
let args = self.args().eval(vm)?;
|
let args = self.args().eval(vm)?;
|
||||||
@ -733,7 +733,7 @@ impl Eval for SetExpr {
|
|||||||
impl Eval for ShowExpr {
|
impl Eval for ShowExpr {
|
||||||
type Output = Recipe;
|
type Output = Recipe;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
// Evaluate the target function.
|
// Evaluate the target function.
|
||||||
let pattern = self.pattern();
|
let pattern = self.pattern();
|
||||||
let pattern = pattern.eval(vm)?.cast::<Pattern>().at(pattern.span())?;
|
let pattern = pattern.eval(vm)?.cast::<Pattern>().at(pattern.span())?;
|
||||||
@ -770,7 +770,7 @@ impl Eval for ShowExpr {
|
|||||||
impl Eval for IfExpr {
|
impl Eval for IfExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let condition = self.condition();
|
let condition = self.condition();
|
||||||
if condition.eval(vm)?.cast::<bool>().at(condition.span())? {
|
if condition.eval(vm)?.cast::<bool>().at(condition.span())? {
|
||||||
self.if_body().eval(vm)
|
self.if_body().eval(vm)
|
||||||
@ -785,7 +785,7 @@ impl Eval for IfExpr {
|
|||||||
impl Eval for WhileExpr {
|
impl Eval for WhileExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let flow = vm.flow.take();
|
let flow = vm.flow.take();
|
||||||
let mut output = Value::None;
|
let mut output = Value::None;
|
||||||
|
|
||||||
@ -817,7 +817,7 @@ impl Eval for WhileExpr {
|
|||||||
impl Eval for ForExpr {
|
impl Eval for ForExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let flow = vm.flow.take();
|
let flow = vm.flow.take();
|
||||||
let mut output = Value::None;
|
let mut output = Value::None;
|
||||||
vm.scopes.enter();
|
vm.scopes.enter();
|
||||||
@ -896,7 +896,7 @@ impl Eval for ForExpr {
|
|||||||
impl Eval for ImportExpr {
|
impl Eval for ImportExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let span = self.path().span();
|
let span = self.path().span();
|
||||||
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
|
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
|
||||||
let module = import(vm, &path, span)?;
|
let module = import(vm, &path, span)?;
|
||||||
@ -925,7 +925,7 @@ impl Eval for ImportExpr {
|
|||||||
impl Eval for IncludeExpr {
|
impl Eval for IncludeExpr {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let span = self.path().span();
|
let span = self.path().span();
|
||||||
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
|
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
|
||||||
let module = import(vm, &path, span)?;
|
let module = import(vm, &path, span)?;
|
||||||
@ -934,14 +934,10 @@ impl Eval for IncludeExpr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Process an import of a module relative to the current location.
|
/// Process an import of a module relative to the current location.
|
||||||
fn import(vm: &mut Vm, path: &str, span: Span) -> TypResult<Module> {
|
fn import(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> {
|
||||||
// Load the source file.
|
// Load the source file.
|
||||||
let full = vm.locate(&path).at(span)?;
|
let full = vm.locate(&path).at(span)?;
|
||||||
let id = vm
|
let id = vm.world.resolve(&full).at(span)?;
|
||||||
.world
|
|
||||||
.resolve(&full)
|
|
||||||
.map_err(|err| failed_to_load("source file", &full, err))
|
|
||||||
.at(span)?;
|
|
||||||
|
|
||||||
// Prevent cyclic importing.
|
// Prevent cyclic importing.
|
||||||
if vm.route.contains(&id) {
|
if vm.route.contains(&id) {
|
||||||
@ -959,7 +955,7 @@ fn import(vm: &mut Vm, path: &str, span: Span) -> TypResult<Module> {
|
|||||||
impl Eval for BreakExpr {
|
impl Eval for BreakExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
if vm.flow.is_none() {
|
if vm.flow.is_none() {
|
||||||
vm.flow = Some(Flow::Break(self.span()));
|
vm.flow = Some(Flow::Break(self.span()));
|
||||||
}
|
}
|
||||||
@ -970,7 +966,7 @@ impl Eval for BreakExpr {
|
|||||||
impl Eval for ContinueExpr {
|
impl Eval for ContinueExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
if vm.flow.is_none() {
|
if vm.flow.is_none() {
|
||||||
vm.flow = Some(Flow::Continue(self.span()));
|
vm.flow = Some(Flow::Continue(self.span()));
|
||||||
}
|
}
|
||||||
@ -981,7 +977,7 @@ impl Eval for ContinueExpr {
|
|||||||
impl Eval for ReturnExpr {
|
impl Eval for ReturnExpr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let value = self.body().map(|body| body.eval(vm)).transpose()?;
|
let value = self.body().map(|body| body.eval(vm)).transpose()?;
|
||||||
if vm.flow.is_none() {
|
if vm.flow.is_none() {
|
||||||
vm.flow = Some(Flow::Return(self.span(), value));
|
vm.flow = Some(Flow::Return(self.span(), value));
|
||||||
@ -993,11 +989,11 @@ impl Eval for ReturnExpr {
|
|||||||
/// Access an expression mutably.
|
/// Access an expression mutably.
|
||||||
pub trait Access {
|
pub trait Access {
|
||||||
/// Access the value.
|
/// Access the value.
|
||||||
fn access<'a>(&self, vm: &'a mut Vm) -> TypResult<&'a mut Value>;
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Access for Expr {
|
impl Access for Expr {
|
||||||
fn access<'a>(&self, vm: &'a mut Vm) -> TypResult<&'a mut Value> {
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||||
match self {
|
match self {
|
||||||
Expr::Ident(v) => v.access(vm),
|
Expr::Ident(v) => v.access(vm),
|
||||||
Expr::FieldAccess(v) => v.access(vm),
|
Expr::FieldAccess(v) => v.access(vm),
|
||||||
@ -1008,13 +1004,13 @@ impl Access for Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Access for Ident {
|
impl Access for Ident {
|
||||||
fn access<'a>(&self, vm: &'a mut Vm) -> TypResult<&'a mut Value> {
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||||
vm.scopes.get_mut(self).at(self.span())
|
vm.scopes.get_mut(self).at(self.span())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Access for FieldAccess {
|
impl Access for FieldAccess {
|
||||||
fn access<'a>(&self, vm: &'a mut Vm) -> TypResult<&'a mut Value> {
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||||
Ok(match self.object().access(vm)? {
|
Ok(match self.object().access(vm)? {
|
||||||
Value::Dict(dict) => dict.get_mut(self.field().take().into()),
|
Value::Dict(dict) => dict.get_mut(self.field().take().into()),
|
||||||
v => bail!(
|
v => bail!(
|
||||||
@ -1027,7 +1023,7 @@ impl Access for FieldAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Access for FuncCall {
|
impl Access for FuncCall {
|
||||||
fn access<'a>(&self, vm: &'a mut Vm) -> TypResult<&'a mut Value> {
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||||
let args = self.args().eval(vm)?;
|
let args = self.args().eval(vm)?;
|
||||||
Ok(match self.callee().access(vm)? {
|
Ok(match self.callee().access(vm)? {
|
||||||
Value::Array(array) => array.get_mut(args.into_index()?).at(self.span())?,
|
Value::Array(array) => array.get_mut(args.into_index()?).at(self.span())?,
|
||||||
|
@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use super::{Args, Func, Node, Value, Vm};
|
use super::{Args, Func, Node, Value, Vm};
|
||||||
use crate::diag::{StrResult, TypResult};
|
use crate::diag::{SourceResult, StrResult};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// A stack of scopes.
|
/// A stack of scopes.
|
||||||
@ -78,7 +78,7 @@ impl Scope {
|
|||||||
pub fn def_fn(
|
pub fn def_fn(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
func: fn(&mut Vm, &mut Args) -> TypResult<Value>,
|
func: fn(&mut Vm, &mut Args) -> SourceResult<Value>,
|
||||||
) {
|
) {
|
||||||
self.define(name, Func::from_fn(name, func));
|
self.define(name, Func::from_fn(name, func));
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::{Scopes, Value};
|
use super::{Scopes, Value};
|
||||||
use crate::diag::{StrResult, TypError};
|
use crate::diag::{SourceError, StrResult};
|
||||||
use crate::source::SourceId;
|
use crate::source::SourceId;
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::util::PathExt;
|
use crate::util::PathExt;
|
||||||
@ -56,7 +56,7 @@ pub enum Flow {
|
|||||||
|
|
||||||
impl Flow {
|
impl Flow {
|
||||||
/// Return an error stating that this control flow is forbidden.
|
/// Return an error stating that this control flow is forbidden.
|
||||||
pub fn forbidden(&self) -> TypError {
|
pub fn forbidden(&self) -> SourceError {
|
||||||
match *self {
|
match *self {
|
||||||
Self::Break(span) => {
|
Self::Break(span) => {
|
||||||
error!(span, "cannot break outside of loop")
|
error!(span, "cannot break outside of loop")
|
||||||
|
@ -4,7 +4,7 @@ use image::{DynamicImage, GenericImageView, ImageResult, Rgba};
|
|||||||
use pdf_writer::{Filter, Finish};
|
use pdf_writer::{Filter, Finish};
|
||||||
|
|
||||||
use super::{deflate, PdfContext, RefExt};
|
use super::{deflate, PdfContext, RefExt};
|
||||||
use crate::image::{DecodedImage, ImageFormat};
|
use crate::image::{DecodedImage, RasterFormat};
|
||||||
|
|
||||||
/// Embed all used images into the PDF.
|
/// Embed all used images into the PDF.
|
||||||
pub fn write_images(ctx: &mut PdfContext) {
|
pub fn write_images(ctx: &mut PdfContext) {
|
||||||
@ -18,11 +18,9 @@ pub fn write_images(ctx: &mut PdfContext) {
|
|||||||
// Add the primary image.
|
// Add the primary image.
|
||||||
// TODO: Error if image could not be encoded.
|
// TODO: Error if image could not be encoded.
|
||||||
match image.decode().unwrap() {
|
match image.decode().unwrap() {
|
||||||
DecodedImage::Raster(dynamic) => {
|
DecodedImage::Raster(dynamic, format) => {
|
||||||
// TODO: Error if image could not be encoded.
|
// TODO: Error if image could not be encoded.
|
||||||
let (data, filter, has_color) =
|
let (data, filter, has_color) = encode_image(format, &dynamic).unwrap();
|
||||||
encode_image(image.format(), &dynamic).unwrap();
|
|
||||||
|
|
||||||
let mut image = ctx.writer.image_xobject(image_ref, &data);
|
let mut image = ctx.writer.image_xobject(image_ref, &data);
|
||||||
image.filter(filter);
|
image.filter(filter);
|
||||||
image.width(width as i32);
|
image.width(width as i32);
|
||||||
@ -70,19 +68,19 @@ pub fn write_images(ctx: &mut PdfContext) {
|
|||||||
///
|
///
|
||||||
/// Skips the alpha channel as that's encoded separately.
|
/// Skips the alpha channel as that's encoded separately.
|
||||||
fn encode_image(
|
fn encode_image(
|
||||||
format: ImageFormat,
|
format: RasterFormat,
|
||||||
dynamic: &DynamicImage,
|
dynamic: &DynamicImage,
|
||||||
) -> ImageResult<(Vec<u8>, Filter, bool)> {
|
) -> ImageResult<(Vec<u8>, Filter, bool)> {
|
||||||
Ok(match (format, dynamic) {
|
Ok(match (format, dynamic) {
|
||||||
// 8-bit gray JPEG.
|
// 8-bit gray JPEG.
|
||||||
(ImageFormat::Jpg, DynamicImage::ImageLuma8(_)) => {
|
(RasterFormat::Jpg, DynamicImage::ImageLuma8(_)) => {
|
||||||
let mut data = Cursor::new(vec![]);
|
let mut data = Cursor::new(vec![]);
|
||||||
dynamic.write_to(&mut data, image::ImageFormat::Jpeg)?;
|
dynamic.write_to(&mut data, image::ImageFormat::Jpeg)?;
|
||||||
(data.into_inner(), Filter::DctDecode, false)
|
(data.into_inner(), Filter::DctDecode, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8-bit RGB JPEG (CMYK JPEGs get converted to RGB earlier).
|
// 8-bit RGB JPEG (CMYK JPEGs get converted to RGB earlier).
|
||||||
(ImageFormat::Jpg, DynamicImage::ImageRgb8(_)) => {
|
(RasterFormat::Jpg, DynamicImage::ImageRgb8(_)) => {
|
||||||
let mut data = Cursor::new(vec![]);
|
let mut data = Cursor::new(vec![]);
|
||||||
dynamic.write_to(&mut data, image::ImageFormat::Jpeg)?;
|
dynamic.write_to(&mut data, image::ImageFormat::Jpeg)?;
|
||||||
(data.into_inner(), Filter::DctDecode, true)
|
(data.into_inner(), Filter::DctDecode, true)
|
||||||
@ -91,7 +89,7 @@ fn encode_image(
|
|||||||
// TODO: Encode flate streams with PNG-predictor?
|
// TODO: Encode flate streams with PNG-predictor?
|
||||||
|
|
||||||
// 8-bit gray PNG.
|
// 8-bit gray PNG.
|
||||||
(ImageFormat::Png, DynamicImage::ImageLuma8(luma)) => {
|
(RasterFormat::Png, DynamicImage::ImageLuma8(luma)) => {
|
||||||
let data = deflate(luma.as_raw());
|
let data = deflate(luma.as_raw());
|
||||||
(data, Filter::FlateDecode, false)
|
(data, Filter::FlateDecode, false)
|
||||||
}
|
}
|
||||||
|
@ -188,10 +188,7 @@ fn render_bitmap_glyph(
|
|||||||
let size = text.size.to_f32();
|
let size = text.size.to_f32();
|
||||||
let ppem = size * ts.sy;
|
let ppem = size * ts.sy;
|
||||||
let raster = text.font.ttf().glyph_raster_image(id, ppem as u16)?;
|
let raster = text.font.ttf().glyph_raster_image(id, ppem as u16)?;
|
||||||
let ext = match raster.format {
|
let image = Image::new(raster.data.into(), raster.format.into()).ok()?;
|
||||||
ttf_parser::RasterImageFormat::PNG => "png",
|
|
||||||
};
|
|
||||||
let image = Image::new(raster.data.into(), ext).ok()?;
|
|
||||||
|
|
||||||
// FIXME: Vertical alignment isn't quite right for Apple Color Emoji,
|
// FIXME: Vertical alignment isn't quite right for Apple Color Emoji,
|
||||||
// and maybe also for Noto Color Emoji. And: Is the size calculation
|
// and maybe also for Noto Color Emoji. And: Is the size calculation
|
||||||
@ -342,7 +339,7 @@ fn render_image(
|
|||||||
|
|
||||||
let mut pixmap = sk::Pixmap::new(w, h)?;
|
let mut pixmap = sk::Pixmap::new(w, h)?;
|
||||||
match image.decode().unwrap() {
|
match image.decode().unwrap() {
|
||||||
DecodedImage::Raster(dynamic) => {
|
DecodedImage::Raster(dynamic, _) => {
|
||||||
let downscale = w < image.width();
|
let downscale = w < image.width();
|
||||||
let filter = if downscale {
|
let filter = if downscale {
|
||||||
FilterType::Lanczos3
|
FilterType::Lanczos3
|
||||||
|
184
src/image.rs
184
src/image.rs
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
use crate::diag::StrResult;
|
||||||
use crate::util::Buffer;
|
use crate::util::Buffer;
|
||||||
|
|
||||||
/// A raster or vector image.
|
/// A raster or vector image.
|
||||||
@ -19,61 +20,26 @@ pub struct Image {
|
|||||||
height: u32,
|
height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A decoded image.
|
|
||||||
pub enum DecodedImage {
|
|
||||||
/// A pixel raster format, like PNG or JPEG.
|
|
||||||
Raster(image::DynamicImage),
|
|
||||||
/// An SVG vector graphic.
|
|
||||||
Svg(usvg::Tree),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A raster or vector image format.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub enum ImageFormat {
|
|
||||||
/// Raster format for illustrations and transparent graphics.
|
|
||||||
Png,
|
|
||||||
/// Lossy raster format suitable for photos.
|
|
||||||
Jpg,
|
|
||||||
/// Raster format that is typically used for short animated clips.
|
|
||||||
Gif,
|
|
||||||
/// The vector graphics format of the web.
|
|
||||||
Svg,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
/// Create an image from a raw buffer and a file extension.
|
/// Create an image from a buffer and a format.
|
||||||
///
|
///
|
||||||
/// The file extension is used to determine the format.
|
/// Extracts the width and height.
|
||||||
pub fn new(data: Buffer, ext: &str) -> io::Result<Self> {
|
pub fn new(data: Buffer, format: ImageFormat) -> StrResult<Self> {
|
||||||
let format = match ext {
|
|
||||||
"svg" | "svgz" => ImageFormat::Svg,
|
|
||||||
"png" => ImageFormat::Png,
|
|
||||||
"jpg" | "jpeg" => ImageFormat::Jpg,
|
|
||||||
"gif" => ImageFormat::Gif,
|
|
||||||
_ => {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidData,
|
|
||||||
"unknown image format",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (width, height) = match format {
|
let (width, height) = match format {
|
||||||
ImageFormat::Svg => {
|
ImageFormat::Vector(VectorFormat::Svg) => {
|
||||||
let opts = usvg::Options::default();
|
let opts = usvg::Options::default();
|
||||||
let tree =
|
let tree = usvg::Tree::from_data(&data, &opts.to_ref())
|
||||||
usvg::Tree::from_data(&data, &opts.to_ref()).map_err(invalid)?;
|
.map_err(format_usvg_error)?;
|
||||||
|
|
||||||
let size = tree.svg_node().size;
|
let size = tree.svg_node().size;
|
||||||
let width = size.width().ceil() as u32;
|
let width = size.width().ceil() as u32;
|
||||||
let height = size.height().ceil() as u32;
|
let height = size.height().ceil() as u32;
|
||||||
(width, height)
|
(width, height)
|
||||||
}
|
}
|
||||||
_ => {
|
ImageFormat::Raster(format) => {
|
||||||
let cursor = io::Cursor::new(&data);
|
let cursor = io::Cursor::new(&data);
|
||||||
let format = convert_format(format);
|
let reader = image::io::Reader::with_format(cursor, format.into());
|
||||||
let reader = image::io::Reader::with_format(cursor, format);
|
reader.into_dimensions().map_err(format_image_error)?
|
||||||
reader.into_dimensions().map_err(invalid)?
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -101,39 +67,125 @@ impl Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decode the image.
|
/// Decode the image.
|
||||||
pub fn decode(&self) -> io::Result<DecodedImage> {
|
pub fn decode(&self) -> StrResult<DecodedImage> {
|
||||||
Ok(match self.format {
|
Ok(match self.format {
|
||||||
ImageFormat::Svg => {
|
ImageFormat::Vector(VectorFormat::Svg) => {
|
||||||
let opts = usvg::Options::default();
|
let opts = usvg::Options::default();
|
||||||
let tree =
|
let tree = usvg::Tree::from_data(&self.data, &opts.to_ref())
|
||||||
usvg::Tree::from_data(&self.data, &opts.to_ref()).map_err(invalid)?;
|
.map_err(format_usvg_error)?;
|
||||||
DecodedImage::Svg(tree)
|
DecodedImage::Svg(tree)
|
||||||
}
|
}
|
||||||
_ => {
|
ImageFormat::Raster(format) => {
|
||||||
let cursor = io::Cursor::new(&self.data);
|
let cursor = io::Cursor::new(&self.data);
|
||||||
let format = convert_format(self.format);
|
let reader = image::io::Reader::with_format(cursor, format.into());
|
||||||
let reader = image::io::Reader::with_format(cursor, format);
|
let dynamic = reader.decode().map_err(format_image_error)?;
|
||||||
let dynamic = reader.decode().map_err(invalid)?;
|
DecodedImage::Raster(dynamic, format)
|
||||||
DecodedImage::Raster(dynamic)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a raster image format to the image crate's format.
|
/// A raster or vector image format.
|
||||||
fn convert_format(format: ImageFormat) -> image::ImageFormat {
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
match format {
|
pub enum ImageFormat {
|
||||||
ImageFormat::Png => image::ImageFormat::Png,
|
/// A raster graphics format.
|
||||||
ImageFormat::Jpg => image::ImageFormat::Jpeg,
|
Raster(RasterFormat),
|
||||||
ImageFormat::Gif => image::ImageFormat::Gif,
|
/// A vector graphics format.
|
||||||
ImageFormat::Svg => panic!("must be a raster format"),
|
Vector(VectorFormat),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A raster graphics format.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum RasterFormat {
|
||||||
|
/// Raster format for illustrations and transparent graphics.
|
||||||
|
Png,
|
||||||
|
/// Lossy raster format suitable for photos.
|
||||||
|
Jpg,
|
||||||
|
/// Raster format that is typically used for short animated clips.
|
||||||
|
Gif,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A vector graphics format.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum VectorFormat {
|
||||||
|
/// The vector graphics format of the web.
|
||||||
|
Svg,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RasterFormat> for image::ImageFormat {
|
||||||
|
fn from(format: RasterFormat) -> Self {
|
||||||
|
match format {
|
||||||
|
RasterFormat::Png => image::ImageFormat::Png,
|
||||||
|
RasterFormat::Jpg => image::ImageFormat::Jpeg,
|
||||||
|
RasterFormat::Gif => image::ImageFormat::Gif,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn any error into an I/O error.
|
impl From<ttf_parser::RasterImageFormat> for RasterFormat {
|
||||||
fn invalid<E>(error: E) -> io::Error
|
fn from(format: ttf_parser::RasterImageFormat) -> Self {
|
||||||
where
|
match format {
|
||||||
E: std::error::Error + Send + Sync + 'static,
|
ttf_parser::RasterImageFormat::PNG => RasterFormat::Png,
|
||||||
{
|
}
|
||||||
io::Error::new(io::ErrorKind::InvalidData, error)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ttf_parser::RasterImageFormat> for ImageFormat {
|
||||||
|
fn from(format: ttf_parser::RasterImageFormat) -> Self {
|
||||||
|
Self::Raster(format.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A decoded image.
|
||||||
|
pub enum DecodedImage {
|
||||||
|
/// A decoded pixel raster.
|
||||||
|
Raster(image::DynamicImage, RasterFormat),
|
||||||
|
/// An decoded SVG tree.
|
||||||
|
Svg(usvg::Tree),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format the user-facing raster graphic decoding error message.
|
||||||
|
fn format_image_error(error: image::ImageError) -> String {
|
||||||
|
match error {
|
||||||
|
image::ImageError::Limits(_) => "file is too large".into(),
|
||||||
|
_ => "failed to decode image".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format the user-facing SVG decoding error message.
|
||||||
|
fn format_usvg_error(error: usvg::Error) -> String {
|
||||||
|
match error {
|
||||||
|
usvg::Error::NotAnUtf8Str => "file is not valid utf-8".into(),
|
||||||
|
usvg::Error::MalformedGZip => "file is not compressed correctly".into(),
|
||||||
|
usvg::Error::ElementsLimitReached => "file is too large".into(),
|
||||||
|
usvg::Error::InvalidSize => {
|
||||||
|
"failed to parse svg: width, height, or viewbox is invalid".into()
|
||||||
|
}
|
||||||
|
usvg::Error::ParsingFailed(error) => match error {
|
||||||
|
roxmltree::Error::UnexpectedCloseTag { expected, actual, pos } => {
|
||||||
|
format!(
|
||||||
|
"failed to parse svg: found closing tag '{actual}' \
|
||||||
|
instead of '{expected}' in line {}",
|
||||||
|
pos.row
|
||||||
|
)
|
||||||
|
}
|
||||||
|
roxmltree::Error::UnknownEntityReference(entity, pos) => {
|
||||||
|
format!(
|
||||||
|
"failed to parse svg: unknown entity '{entity}' in line {}",
|
||||||
|
pos.row
|
||||||
|
)
|
||||||
|
}
|
||||||
|
roxmltree::Error::DuplicatedAttribute(attr, pos) => {
|
||||||
|
format!(
|
||||||
|
"failed to parse svg: duplicate attribute '{attr}' in line {}",
|
||||||
|
pos.row
|
||||||
|
)
|
||||||
|
}
|
||||||
|
roxmltree::Error::NoRootNode => {
|
||||||
|
"failed to parse svg: missing root node".into()
|
||||||
|
}
|
||||||
|
roxmltree::Error::SizeLimit => "file is too large".into(),
|
||||||
|
_ => "failed to parse svg".into(),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
17
src/lib.rs
17
src/lib.rs
@ -49,10 +49,9 @@ pub mod parse;
|
|||||||
pub mod source;
|
pub mod source;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use crate::diag::TypResult;
|
use crate::diag::{FileResult, SourceResult};
|
||||||
use crate::eval::Scope;
|
use crate::eval::Scope;
|
||||||
use crate::font::{Font, FontBook};
|
use crate::font::{Font, FontBook};
|
||||||
use crate::frame::Frame;
|
use crate::frame::Frame;
|
||||||
@ -65,7 +64,7 @@ use crate::util::Buffer;
|
|||||||
/// Returns either a vector of frames representing individual pages or
|
/// Returns either a vector of frames representing individual pages or
|
||||||
/// diagnostics in the form of a vector of error message with file and span
|
/// diagnostics in the form of a vector of error message with file and span
|
||||||
/// information.
|
/// information.
|
||||||
pub fn typeset(world: &dyn World, main: SourceId) -> TypResult<Vec<Frame>> {
|
pub fn typeset(world: &dyn World, main: SourceId) -> SourceResult<Vec<Frame>> {
|
||||||
let module = eval::evaluate(world, main, vec![])?;
|
let module = eval::evaluate(world, main, vec![])?;
|
||||||
model::layout(world, &module.content)
|
model::layout(world, &module.content)
|
||||||
}
|
}
|
||||||
@ -75,8 +74,8 @@ pub trait World {
|
|||||||
/// Access the global configuration.
|
/// Access the global configuration.
|
||||||
fn config(&self) -> &Config;
|
fn config(&self) -> &Config;
|
||||||
|
|
||||||
/// Resolve the unique id of a source file.
|
/// Try to resolve the unique id of a source file.
|
||||||
fn resolve(&self, path: &Path) -> io::Result<SourceId>;
|
fn resolve(&self, path: &Path) -> FileResult<SourceId>;
|
||||||
|
|
||||||
/// Access a source file by id.
|
/// Access a source file by id.
|
||||||
fn source(&self, id: SourceId) -> &Source;
|
fn source(&self, id: SourceId) -> &Source;
|
||||||
@ -84,11 +83,11 @@ pub trait World {
|
|||||||
/// Metadata about all known fonts.
|
/// Metadata about all known fonts.
|
||||||
fn book(&self) -> &FontBook;
|
fn book(&self) -> &FontBook;
|
||||||
|
|
||||||
/// Access the font with the given id.
|
/// Try to access the font with the given id.
|
||||||
fn font(&self, id: usize) -> io::Result<Font>;
|
fn font(&self, id: usize) -> Option<Font>;
|
||||||
|
|
||||||
/// Access a file at a path.
|
/// Try to access a file at a path.
|
||||||
fn file(&self, path: &Path) -> io::Result<Buffer>;
|
fn file(&self, path: &Path) -> FileResult<Buffer>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The global configuration for typesetting.
|
/// The global configuration for typesetting.
|
||||||
|
@ -6,7 +6,7 @@ pub struct HideNode(pub LayoutNode);
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl HideNode {
|
impl HideNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::inline(Self(args.expect("body")?)))
|
Ok(Content::inline(Self(args.expect("body")?)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17,7 +17,7 @@ impl Layout for HideNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let mut frames = self.0.layout(world, regions, styles)?;
|
let mut frames = self.0.layout(world, regions, styles)?;
|
||||||
for frame in &mut frames {
|
for frame in &mut frames {
|
||||||
frame.clear();
|
frame.clear();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
use crate::image::Image;
|
use crate::image::{Image, ImageFormat, RasterFormat, VectorFormat};
|
||||||
use crate::library::prelude::*;
|
use crate::library::prelude::*;
|
||||||
use crate::library::text::TextNode;
|
use crate::library::text::TextNode;
|
||||||
|
|
||||||
@ -13,19 +13,22 @@ impl ImageNode {
|
|||||||
/// How the image should adjust itself to a given area.
|
/// How the image should adjust itself to a given area.
|
||||||
pub const FIT: ImageFit = ImageFit::Cover;
|
pub const FIT: ImageFit = ImageFit::Cover;
|
||||||
|
|
||||||
fn construct(vm: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let Spanned { v: path, span } =
|
let Spanned { v: path, span } =
|
||||||
args.expect::<Spanned<EcoString>>("path to image file")?;
|
args.expect::<Spanned<EcoString>>("path to image file")?;
|
||||||
|
|
||||||
let full = vm.locate(&path).at(span)?;
|
let full = vm.locate(&path).at(span)?;
|
||||||
|
let buffer = vm.world.file(&full).at(span)?;
|
||||||
let ext = full.extension().and_then(OsStr::to_str).unwrap_or_default();
|
let ext = full.extension().and_then(OsStr::to_str).unwrap_or_default();
|
||||||
let image = vm
|
let format = match ext.to_lowercase().as_str() {
|
||||||
.world
|
"png" => ImageFormat::Raster(RasterFormat::Png),
|
||||||
.file(&full)
|
"jpg" | "jpeg" => ImageFormat::Raster(RasterFormat::Jpg),
|
||||||
.and_then(|buffer| Image::new(buffer, ext))
|
"gif" => ImageFormat::Raster(RasterFormat::Gif),
|
||||||
.map_err(|err| failed_to_load("image", &full, err))
|
"svg" | "svgz" => ImageFormat::Vector(VectorFormat::Svg),
|
||||||
.at(span)?;
|
_ => bail!(span, "unknown image format"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let image = Image::new(buffer, format).at(span)?;
|
||||||
let width = args.named("width")?;
|
let width = args.named("width")?;
|
||||||
let height = args.named("height")?;
|
let height = args.named("height")?;
|
||||||
|
|
||||||
@ -41,7 +44,7 @@ impl Layout for ImageNode {
|
|||||||
_: &dyn World,
|
_: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let pxw = self.0.width() as f64;
|
let pxw = self.0.width() as f64;
|
||||||
let pxh = self.0.height() as f64;
|
let pxh = self.0.height() as f64;
|
||||||
let px_ratio = pxw / pxh;
|
let px_ratio = pxw / pxh;
|
||||||
|
@ -15,7 +15,7 @@ impl LineNode {
|
|||||||
#[property(resolve, fold)]
|
#[property(resolve, fold)]
|
||||||
pub const STROKE: RawStroke = RawStroke::default();
|
pub const STROKE: RawStroke = RawStroke::default();
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let origin = args.named("origin")?.unwrap_or_default();
|
let origin = args.named("origin")?.unwrap_or_default();
|
||||||
|
|
||||||
let delta = match args.named::<Spec<Relative<RawLength>>>("to")? {
|
let delta = match args.named::<Spec<Relative<RawLength>>>("to")? {
|
||||||
@ -43,7 +43,7 @@ impl Layout for LineNode {
|
|||||||
_: &dyn World,
|
_: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let stroke = styles.get(Self::STROKE).unwrap_or_default();
|
let stroke = styles.get(Self::STROKE).unwrap_or_default();
|
||||||
|
|
||||||
let origin = self
|
let origin = self
|
||||||
|
@ -39,7 +39,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
|
|||||||
pub const RADIUS: Corners<Option<Relative<RawLength>>> =
|
pub const RADIUS: Corners<Option<Relative<RawLength>>> =
|
||||||
Corners::splat(Relative::zero());
|
Corners::splat(Relative::zero());
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let size = match S {
|
let size = match S {
|
||||||
SQUARE => args.named::<RawLength>("size")?.map(Relative::from),
|
SQUARE => args.named::<RawLength>("size")?.map(Relative::from),
|
||||||
CIRCLE => args.named::<RawLength>("radius")?.map(|r| 2.0 * Relative::from(r)),
|
CIRCLE => args.named::<RawLength>("radius")?.map(|r| 2.0 * Relative::from(r)),
|
||||||
@ -81,7 +81,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let mut frames;
|
let mut frames;
|
||||||
if let Some(child) = &self.0 {
|
if let Some(child) = &self.0 {
|
||||||
let mut inset = styles.get(Self::INSET);
|
let mut inset = styles.get(Self::INSET);
|
||||||
|
@ -12,7 +12,7 @@ pub struct MoveNode {
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl MoveNode {
|
impl MoveNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let dx = args.named("dx")?.unwrap_or_default();
|
let dx = args.named("dx")?.unwrap_or_default();
|
||||||
let dy = args.named("dy")?.unwrap_or_default();
|
let dy = args.named("dy")?.unwrap_or_default();
|
||||||
Ok(Content::inline(Self {
|
Ok(Content::inline(Self {
|
||||||
@ -28,7 +28,7 @@ impl Layout for MoveNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let mut frames = self.child.layout(world, regions, styles)?;
|
let mut frames = self.child.layout(world, regions, styles)?;
|
||||||
|
|
||||||
let delta = self.delta.resolve(styles);
|
let delta = self.delta.resolve(styles);
|
||||||
@ -62,7 +62,7 @@ impl<const T: TransformKind> TransformNode<T> {
|
|||||||
#[property(resolve)]
|
#[property(resolve)]
|
||||||
pub const ORIGIN: Spec<Option<RawAlign>> = Spec::default();
|
pub const ORIGIN: Spec<Option<RawAlign>> = Spec::default();
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let transform = match T {
|
let transform = match T {
|
||||||
ROTATE => {
|
ROTATE => {
|
||||||
let angle = args.named_or_find("angle")?.unwrap_or_default();
|
let angle = args.named_or_find("angle")?.unwrap_or_default();
|
||||||
@ -89,7 +89,7 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
|
let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
|
||||||
let mut frames = self.child.layout(world, regions, styles)?;
|
let mut frames = self.child.layout(world, regions, styles)?;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ pub struct AlignNode {
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl AlignNode {
|
impl AlignNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let aligns: Spec<Option<RawAlign>> = args.find()?.unwrap_or_default();
|
let aligns: Spec<Option<RawAlign>> = args.find()?.unwrap_or_default();
|
||||||
let body: Content = args.expect("body")?;
|
let body: Content = args.expect("body")?;
|
||||||
Ok(match (body, aligns) {
|
Ok(match (body, aligns) {
|
||||||
@ -31,7 +31,7 @@ impl Layout for AlignNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// The child only needs to expand along an axis if there's no alignment.
|
// The child only needs to expand along an axis if there's no alignment.
|
||||||
let mut pod = regions.clone();
|
let mut pod = regions.clone();
|
||||||
pod.expand &= self.aligns.map_is_none();
|
pod.expand &= self.aligns.map_is_none();
|
||||||
|
@ -17,7 +17,7 @@ impl ColumnsNode {
|
|||||||
#[property(resolve)]
|
#[property(resolve)]
|
||||||
pub const GUTTER: Relative<RawLength> = Ratio::new(0.04).into();
|
pub const GUTTER: Relative<RawLength> = Ratio::new(0.04).into();
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::block(Self {
|
Ok(Content::block(Self {
|
||||||
columns: args.expect("column count")?,
|
columns: args.expect("column count")?,
|
||||||
child: args.expect("body")?,
|
child: args.expect("body")?,
|
||||||
@ -31,7 +31,7 @@ impl Layout for ColumnsNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// Separating the infinite space into infinite columns does not make
|
// Separating the infinite space into infinite columns does not make
|
||||||
// much sense.
|
// much sense.
|
||||||
if !regions.first.x.is_finite() {
|
if !regions.first.x.is_finite() {
|
||||||
@ -106,7 +106,7 @@ pub struct ColbreakNode;
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl ColbreakNode {
|
impl ColbreakNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let weak = args.named("weak")?.unwrap_or(false);
|
let weak = args.named("weak")?.unwrap_or(false);
|
||||||
Ok(Content::Colbreak { weak })
|
Ok(Content::Colbreak { weak })
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ pub struct BoxNode;
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl BoxNode {
|
impl BoxNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let width = args.named("width")?;
|
let width = args.named("width")?;
|
||||||
let height = args.named("height")?;
|
let height = args.named("height")?;
|
||||||
let body: LayoutNode = args.eat()?.unwrap_or_default();
|
let body: LayoutNode = args.eat()?.unwrap_or_default();
|
||||||
@ -18,7 +18,7 @@ pub struct BlockNode;
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl BlockNode {
|
impl BlockNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::Block(args.eat()?.unwrap_or_default()))
|
Ok(Content::Block(args.eat()?.unwrap_or_default()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ impl Layout for FlowNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let mut layouter = FlowLayouter::new(regions);
|
let mut layouter = FlowLayouter::new(regions);
|
||||||
|
|
||||||
for (child, map) in self.0.iter() {
|
for (child, map) in self.0.iter() {
|
||||||
@ -152,7 +152,7 @@ impl FlowLayouter {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
node: &LayoutNode,
|
node: &LayoutNode,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<()> {
|
) -> SourceResult<()> {
|
||||||
// Don't even try layouting into a full region.
|
// Don't even try layouting into a full region.
|
||||||
if self.regions.is_full() {
|
if self.regions.is_full() {
|
||||||
self.finish_region();
|
self.finish_region();
|
||||||
|
@ -13,7 +13,7 @@ pub struct GridNode {
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl GridNode {
|
impl GridNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let columns = args.named("columns")?.unwrap_or_default();
|
let columns = args.named("columns")?.unwrap_or_default();
|
||||||
let rows = args.named("rows")?.unwrap_or_default();
|
let rows = args.named("rows")?.unwrap_or_default();
|
||||||
let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
|
let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
|
||||||
@ -36,7 +36,7 @@ impl Layout for GridNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// Prepare grid layout by unifying content and gutter tracks.
|
// Prepare grid layout by unifying content and gutter tracks.
|
||||||
let layouter = GridLayouter::new(
|
let layouter = GridLayouter::new(
|
||||||
world,
|
world,
|
||||||
@ -203,7 +203,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the columns sizes and then layouts the grid row-by-row.
|
/// Determines the columns sizes and then layouts the grid row-by-row.
|
||||||
pub fn layout(mut self) -> TypResult<Vec<Frame>> {
|
pub fn layout(mut self) -> SourceResult<Vec<Frame>> {
|
||||||
self.measure_columns()?;
|
self.measure_columns()?;
|
||||||
|
|
||||||
for y in 0 .. self.rows.len() {
|
for y in 0 .. self.rows.len() {
|
||||||
@ -228,7 +228,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Determine all column sizes.
|
/// Determine all column sizes.
|
||||||
fn measure_columns(&mut self) -> TypResult<()> {
|
fn measure_columns(&mut self) -> SourceResult<()> {
|
||||||
// Sum of sizes of resolved relative tracks.
|
// Sum of sizes of resolved relative tracks.
|
||||||
let mut rel = Length::zero();
|
let mut rel = Length::zero();
|
||||||
|
|
||||||
@ -275,7 +275,10 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Measure the size that is available to auto columns.
|
/// Measure the size that is available to auto columns.
|
||||||
fn measure_auto_columns(&mut self, available: Length) -> TypResult<(Length, usize)> {
|
fn measure_auto_columns(
|
||||||
|
&mut self,
|
||||||
|
available: Length,
|
||||||
|
) -> SourceResult<(Length, usize)> {
|
||||||
let mut auto = Length::zero();
|
let mut auto = Length::zero();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
||||||
@ -355,7 +358,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
/// Layout a row with automatic height. Such a row may break across multiple
|
/// Layout a row with automatic height. Such a row may break across multiple
|
||||||
/// regions.
|
/// regions.
|
||||||
fn layout_auto_row(&mut self, y: usize) -> TypResult<()> {
|
fn layout_auto_row(&mut self, y: usize) -> SourceResult<()> {
|
||||||
let mut resolved: Vec<Length> = vec![];
|
let mut resolved: Vec<Length> = vec![];
|
||||||
|
|
||||||
// Determine the size for each region of the row.
|
// Determine the size for each region of the row.
|
||||||
@ -423,7 +426,11 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
/// Layout a row with relative height. Such a row cannot break across
|
/// Layout a row with relative height. Such a row cannot break across
|
||||||
/// multiple regions, but it may force a region break.
|
/// multiple regions, but it may force a region break.
|
||||||
fn layout_relative_row(&mut self, v: Relative<RawLength>, y: usize) -> TypResult<()> {
|
fn layout_relative_row(
|
||||||
|
&mut self,
|
||||||
|
v: Relative<RawLength>,
|
||||||
|
y: usize,
|
||||||
|
) -> SourceResult<()> {
|
||||||
let resolved = v.resolve(self.styles).relative_to(self.regions.base.y);
|
let resolved = v.resolve(self.styles).relative_to(self.regions.base.y);
|
||||||
let frame = self.layout_single_row(resolved, y)?;
|
let frame = self.layout_single_row(resolved, y)?;
|
||||||
|
|
||||||
@ -444,7 +451,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a row with fixed height and return its frame.
|
/// Layout a row with fixed height and return its frame.
|
||||||
fn layout_single_row(&mut self, height: Length, y: usize) -> TypResult<Frame> {
|
fn layout_single_row(&mut self, height: Length, y: usize) -> SourceResult<Frame> {
|
||||||
let mut output = Frame::new(Size::new(self.used.x, height));
|
let mut output = Frame::new(Size::new(self.used.x, height));
|
||||||
|
|
||||||
let mut pos = Point::zero();
|
let mut pos = Point::zero();
|
||||||
@ -483,7 +490,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
heights: &[Length],
|
heights: &[Length],
|
||||||
y: usize,
|
y: usize,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// Prepare frames.
|
// Prepare frames.
|
||||||
let mut outputs: Vec<_> = heights
|
let mut outputs: Vec<_> = heights
|
||||||
.iter()
|
.iter()
|
||||||
@ -535,7 +542,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finish rows for one region.
|
/// Finish rows for one region.
|
||||||
fn finish_region(&mut self) -> TypResult<()> {
|
fn finish_region(&mut self) -> SourceResult<()> {
|
||||||
// Determine the size of the grid in this region, expanding fully if
|
// Determine the size of the grid in this region, expanding fully if
|
||||||
// there are fr rows.
|
// there are fr rows.
|
||||||
let mut size = self.used;
|
let mut size = self.used;
|
||||||
|
@ -11,7 +11,7 @@ pub struct PadNode {
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl PadNode {
|
impl PadNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let all = args.named("rest")?.or(args.find()?);
|
let all = args.named("rest")?.or(args.find()?);
|
||||||
let x = args.named("x")?;
|
let x = args.named("x")?;
|
||||||
let y = args.named("y")?;
|
let y = args.named("y")?;
|
||||||
@ -31,7 +31,7 @@ impl Layout for PadNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// Layout child into padded regions.
|
// Layout child into padded regions.
|
||||||
let padding = self.padding.resolve(styles);
|
let padding = self.padding.resolve(styles);
|
||||||
let pod = regions.map(|size| shrink(size, padding));
|
let pod = regions.map(|size| shrink(size, padding));
|
||||||
|
@ -41,7 +41,7 @@ impl PageNode {
|
|||||||
#[property(referenced)]
|
#[property(referenced)]
|
||||||
pub const FOREGROUND: Marginal = Marginal::None;
|
pub const FOREGROUND: Marginal = Marginal::None;
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::Page(Self(args.expect("body")?)))
|
Ok(Content::Page(Self(args.expect("body")?)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ impl PageNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
mut page: usize,
|
mut page: usize,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// When one of the lengths is infinite the page fits its content along
|
// When one of the lengths is infinite the page fits its content along
|
||||||
// that axis.
|
// that axis.
|
||||||
let width = styles.get(Self::WIDTH).unwrap_or(Length::inf());
|
let width = styles.get(Self::WIDTH).unwrap_or(Length::inf());
|
||||||
@ -159,7 +159,7 @@ pub struct PagebreakNode;
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl PagebreakNode {
|
impl PagebreakNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let weak = args.named("weak")?.unwrap_or(false);
|
let weak = args.named("weak")?.unwrap_or(false);
|
||||||
Ok(Content::Pagebreak { weak })
|
Ok(Content::Pagebreak { weak })
|
||||||
}
|
}
|
||||||
@ -178,7 +178,11 @@ pub enum Marginal {
|
|||||||
|
|
||||||
impl Marginal {
|
impl Marginal {
|
||||||
/// Resolve the marginal based on the page number.
|
/// Resolve the marginal based on the page number.
|
||||||
pub fn resolve(&self, world: &dyn World, page: usize) -> TypResult<Option<Content>> {
|
pub fn resolve(
|
||||||
|
&self,
|
||||||
|
world: &dyn World,
|
||||||
|
page: usize,
|
||||||
|
) -> SourceResult<Option<Content>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::None => None,
|
Self::None => None,
|
||||||
Self::Content(content) => Some(content.clone()),
|
Self::Content(content) => Some(content.clone()),
|
||||||
|
@ -7,7 +7,7 @@ pub struct PlaceNode(pub LayoutNode);
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl PlaceNode {
|
impl PlaceNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let aligns = args.find()?.unwrap_or(Spec::with_x(Some(RawAlign::Start)));
|
let aligns = args.find()?.unwrap_or(Spec::with_x(Some(RawAlign::Start)));
|
||||||
let dx = args.named("dx")?.unwrap_or_default();
|
let dx = args.named("dx")?.unwrap_or_default();
|
||||||
let dy = args.named("dy")?.unwrap_or_default();
|
let dy = args.named("dy")?.unwrap_or_default();
|
||||||
@ -24,7 +24,7 @@ impl Layout for PlaceNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let out_of_flow = self.out_of_flow();
|
let out_of_flow = self.out_of_flow();
|
||||||
|
|
||||||
// The pod is the base area of the region because for absolute
|
// The pod is the base area of the region because for absolute
|
||||||
|
@ -8,7 +8,7 @@ pub struct HNode;
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl HNode {
|
impl HNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let amount = args.expect("spacing")?;
|
let amount = args.expect("spacing")?;
|
||||||
let weak = args.named("weak")?.unwrap_or(false);
|
let weak = args.named("weak")?.unwrap_or(false);
|
||||||
Ok(Content::Horizontal { amount, weak })
|
Ok(Content::Horizontal { amount, weak })
|
||||||
@ -20,7 +20,7 @@ pub struct VNode;
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl VNode {
|
impl VNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let amount = args.expect("spacing")?;
|
let amount = args.expect("spacing")?;
|
||||||
let weak = args.named("weak")?.unwrap_or(false);
|
let weak = args.named("weak")?.unwrap_or(false);
|
||||||
Ok(Content::Vertical { amount, weak, generated: false })
|
Ok(Content::Vertical { amount, weak, generated: false })
|
||||||
|
@ -15,7 +15,7 @@ pub struct StackNode {
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl StackNode {
|
impl StackNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::block(Self {
|
Ok(Content::block(Self {
|
||||||
dir: args.named("dir")?.unwrap_or(Dir::TTB),
|
dir: args.named("dir")?.unwrap_or(Dir::TTB),
|
||||||
spacing: args.named("spacing")?,
|
spacing: args.named("spacing")?,
|
||||||
@ -30,7 +30,7 @@ impl Layout for StackNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let mut layouter = StackLayouter::new(self.dir, regions, styles);
|
let mut layouter = StackLayouter::new(self.dir, regions, styles);
|
||||||
|
|
||||||
// Spacing to insert before the next node.
|
// Spacing to insert before the next node.
|
||||||
@ -171,7 +171,7 @@ impl<'a> StackLayouter<'a> {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
node: &LayoutNode,
|
node: &LayoutNode,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<()> {
|
) -> SourceResult<()> {
|
||||||
if self.regions.is_full() {
|
if self.regions.is_full() {
|
||||||
self.finish_region();
|
self.finish_region();
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ impl MathNode {
|
|||||||
#[property(resolve, shorthand(around))]
|
#[property(resolve, shorthand(around))]
|
||||||
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
|
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show(Self {
|
Ok(Content::show(Self {
|
||||||
formula: args.expect("formula")?,
|
formula: args.expect("formula")?,
|
||||||
display: args.named("display")?.unwrap_or(false),
|
display: args.named("display")?.unwrap_or(false),
|
||||||
@ -48,7 +48,7 @@ impl Show for MathNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, _: &dyn World, styles: StyleChain) -> TypResult<Content> {
|
fn realize(&self, _: &dyn World, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let node = self::rex::RexNode {
|
let node = self::rex::RexNode {
|
||||||
tex: self.formula.clone(),
|
tex: self.formula.clone(),
|
||||||
display: self.display,
|
display: self.display,
|
||||||
@ -67,7 +67,7 @@ impl Show for MathNode {
|
|||||||
_: &dyn World,
|
_: &dyn World,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
mut realized: Content,
|
mut realized: Content,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
let mut map = StyleMap::new();
|
let mut map = StyleMap::new();
|
||||||
map.set_family(styles.get(Self::FAMILY).clone(), styles);
|
map.set_family(styles.get(Self::FAMILY).clone(), styles);
|
||||||
|
|
||||||
|
@ -25,13 +25,13 @@ impl Layout for RexNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
_: &Regions,
|
_: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// Load the font.
|
// Load the font.
|
||||||
let span = self.tex.span;
|
let span = self.tex.span;
|
||||||
let font = world
|
let font = world
|
||||||
.book()
|
.book()
|
||||||
.select(self.family.as_str(), variant(styles))
|
.select(self.family.as_str(), variant(styles))
|
||||||
.and_then(|id| world.font(id).ok())
|
.and_then(|id| world.font(id))
|
||||||
.ok_or("failed to find math font")
|
.ok_or("failed to find math font")
|
||||||
.at(span)?;
|
.at(span)?;
|
||||||
|
|
||||||
|
@ -8,9 +8,7 @@ pub use std::sync::Arc;
|
|||||||
|
|
||||||
pub use typst_macros::node;
|
pub use typst_macros::node;
|
||||||
|
|
||||||
pub use crate::diag::{
|
pub use crate::diag::{with_alternative, At, SourceError, SourceResult, StrResult};
|
||||||
failed_to_load, with_alternative, At, Error, StrResult, TypError, TypResult,
|
|
||||||
};
|
|
||||||
pub use crate::eval::{
|
pub use crate::eval::{
|
||||||
Arg, Args, Array, Cast, Dict, Dynamic, Func, Node, RawAlign, RawLength, RawStroke,
|
Arg, Args, Array, Cast, Dict, Dynamic, Func, Node, RawAlign, RawLength, RawStroke,
|
||||||
Scope, Smart, Value, Vm,
|
Scope, Smart, Value, Vm,
|
||||||
|
@ -7,7 +7,11 @@ pub struct DocNode(pub StyleVec<PageNode>);
|
|||||||
|
|
||||||
impl DocNode {
|
impl DocNode {
|
||||||
/// Layout the document into a sequence of frames, one per page.
|
/// Layout the document into a sequence of frames, one per page.
|
||||||
pub fn layout(&self, world: &dyn World, styles: StyleChain) -> TypResult<Vec<Frame>> {
|
pub fn layout(
|
||||||
|
&self,
|
||||||
|
world: &dyn World,
|
||||||
|
styles: StyleChain,
|
||||||
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let mut frames = vec![];
|
let mut frames = vec![];
|
||||||
for (page, map) in self.0.iter() {
|
for (page, map) in self.0.iter() {
|
||||||
let number = 1 + frames.len();
|
let number = 1 + frames.len();
|
||||||
|
@ -60,7 +60,7 @@ impl HeadingNode {
|
|||||||
/// Whether the heading is numbered.
|
/// Whether the heading is numbered.
|
||||||
pub const NUMBERED: bool = true;
|
pub const NUMBERED: bool = true;
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show(Self {
|
Ok(Content::show(Self {
|
||||||
body: args.expect("body")?,
|
body: args.expect("body")?,
|
||||||
level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()),
|
level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()),
|
||||||
@ -82,7 +82,7 @@ impl Show for HeadingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, _: &dyn World, _: StyleChain) -> TypResult<Content> {
|
fn realize(&self, _: &dyn World, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(Content::block(self.body.clone()))
|
Ok(Content::block(self.body.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ impl Show for HeadingNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
mut realized: Content,
|
mut realized: Content,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
macro_rules! resolve {
|
macro_rules! resolve {
|
||||||
($key:expr) => {
|
($key:expr) => {
|
||||||
styles.get($key).resolve(world, self.level)?
|
styles.get($key).resolve(world, self.level)?
|
||||||
@ -149,7 +149,7 @@ pub enum Leveled<T> {
|
|||||||
|
|
||||||
impl<T: Cast + Clone> Leveled<T> {
|
impl<T: Cast + Clone> Leveled<T> {
|
||||||
/// Resolve the value based on the level.
|
/// Resolve the value based on the level.
|
||||||
pub fn resolve(&self, world: &dyn World, level: NonZeroUsize) -> TypResult<T> {
|
pub fn resolve(&self, world: &dyn World, level: NonZeroUsize) -> SourceResult<T> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Value(value) => value.clone(),
|
Self::Value(value) => value.clone(),
|
||||||
Self::Mapping(mapping) => mapping(level),
|
Self::Mapping(mapping) => mapping(level),
|
||||||
|
@ -56,7 +56,7 @@ impl<const L: ListKind> ListNode<L> {
|
|||||||
#[property(resolve)]
|
#[property(resolve)]
|
||||||
pub const SPACING: BlockSpacing = Ratio::one().into();
|
pub const SPACING: BlockSpacing = Ratio::one().into();
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show(Self {
|
Ok(Content::show(Self {
|
||||||
start: args.named("start")?.unwrap_or(1),
|
start: args.named("start")?.unwrap_or(1),
|
||||||
tight: args.named("tight")?.unwrap_or(true),
|
tight: args.named("tight")?.unwrap_or(true),
|
||||||
@ -100,7 +100,7 @@ impl<const L: ListKind> Show for ListNode<L> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, world: &dyn World, styles: StyleChain) -> TypResult<Content> {
|
fn realize(&self, world: &dyn World, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
let mut number = self.start;
|
let mut number = self.start;
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ impl<const L: ListKind> Show for ListNode<L> {
|
|||||||
_: &dyn World,
|
_: &dyn World,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
realized: Content,
|
realized: Content,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
let mut above = styles.get(Self::ABOVE);
|
let mut above = styles.get(Self::ABOVE);
|
||||||
let mut below = styles.get(Self::BELOW);
|
let mut below = styles.get(Self::BELOW);
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ impl Label {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
kind: ListKind,
|
kind: ListKind,
|
||||||
number: usize,
|
number: usize,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Default => match kind {
|
Self::Default => match kind {
|
||||||
UNORDERED => Content::Text('•'.into()),
|
UNORDERED => Content::Text('•'.into()),
|
||||||
|
@ -6,7 +6,7 @@ pub struct RefNode(pub EcoString);
|
|||||||
|
|
||||||
#[node(showable)]
|
#[node(showable)]
|
||||||
impl RefNode {
|
impl RefNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show(Self(args.expect("label")?)))
|
Ok(Content::show(Self(args.expect("label")?)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,7 +22,7 @@ impl Show for RefNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, _: &dyn World, _: StyleChain) -> TypResult<Content> {
|
fn realize(&self, _: &dyn World, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(Content::Text(format_eco!("@{}", self.0)))
|
Ok(Content::Text(format_eco!("@{}", self.0)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ impl TableNode {
|
|||||||
#[property(resolve, shorthand(around))]
|
#[property(resolve, shorthand(around))]
|
||||||
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
|
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let columns = args.named("columns")?.unwrap_or_default();
|
let columns = args.named("columns")?.unwrap_or_default();
|
||||||
let rows = args.named("rows")?.unwrap_or_default();
|
let rows = args.named("rows")?.unwrap_or_default();
|
||||||
let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
|
let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
|
||||||
@ -72,7 +72,7 @@ impl Show for TableNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, world: &dyn World, styles: StyleChain) -> TypResult<Content> {
|
fn realize(&self, world: &dyn World, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let fill = styles.get(Self::FILL);
|
let fill = styles.get(Self::FILL);
|
||||||
let stroke = styles.get(Self::STROKE).map(RawStroke::unwrap_or_default);
|
let stroke = styles.get(Self::STROKE).map(RawStroke::unwrap_or_default);
|
||||||
let padding = styles.get(Self::PADDING);
|
let padding = styles.get(Self::PADDING);
|
||||||
@ -98,7 +98,7 @@ impl Show for TableNode {
|
|||||||
|
|
||||||
Ok(child)
|
Ok(child)
|
||||||
})
|
})
|
||||||
.collect::<TypResult<_>>()?;
|
.collect::<SourceResult<_>>()?;
|
||||||
|
|
||||||
Ok(Content::block(GridNode {
|
Ok(Content::block(GridNode {
|
||||||
tracks: self.tracks.clone(),
|
tracks: self.tracks.clone(),
|
||||||
@ -113,7 +113,7 @@ impl Show for TableNode {
|
|||||||
_: &dyn World,
|
_: &dyn World,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
realized: Content,
|
realized: Content,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
Ok(realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW)))
|
Ok(realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,7 +129,7 @@ pub enum Celled<T> {
|
|||||||
|
|
||||||
impl<T: Cast + Clone> Celled<T> {
|
impl<T: Cast + Clone> Celled<T> {
|
||||||
/// Resolve the value based on the cell position.
|
/// Resolve the value based on the cell position.
|
||||||
pub fn resolve(&self, world: &dyn World, x: usize, y: usize) -> TypResult<T> {
|
pub fn resolve(&self, world: &dyn World, x: usize, y: usize) -> SourceResult<T> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Value(value) => value.clone(),
|
Self::Value(value) => value.clone(),
|
||||||
Self::Func(func, span) => {
|
Self::Func(func, span) => {
|
||||||
|
@ -34,7 +34,7 @@ impl<const L: DecoLine> DecoNode<L> {
|
|||||||
/// with the glyphs. Does not apply to strikethrough.
|
/// with the glyphs. Does not apply to strikethrough.
|
||||||
pub const EVADE: bool = true;
|
pub const EVADE: bool = true;
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show(Self(args.expect("body")?)))
|
Ok(Content::show(Self(args.expect("body")?)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ impl<const L: DecoLine> Show for DecoNode<L> {
|
|||||||
dict! { "body" => Value::Content(self.0.clone()) }
|
dict! { "body" => Value::Content(self.0.clone()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, _: &dyn World, styles: StyleChain) -> TypResult<Content> {
|
fn realize(&self, _: &dyn World, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.0.clone().styled(TextNode::DECO, Decoration {
|
Ok(self.0.clone().styled(TextNode::DECO, Decoration {
|
||||||
line: L,
|
line: L,
|
||||||
stroke: styles.get(Self::STROKE).unwrap_or_default(),
|
stroke: styles.get(Self::STROKE).unwrap_or_default(),
|
||||||
|
@ -18,7 +18,7 @@ impl LinkNode {
|
|||||||
/// Whether to underline the link.
|
/// Whether to underline the link.
|
||||||
pub const UNDERLINE: Smart<bool> = Smart::Auto;
|
pub const UNDERLINE: Smart<bool> = Smart::Auto;
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show({
|
Ok(Content::show({
|
||||||
let dest = args.expect::<Destination>("destination")?;
|
let dest = args.expect::<Destination>("destination")?;
|
||||||
let body = match dest {
|
let body = match dest {
|
||||||
@ -64,7 +64,7 @@ impl Show for LinkNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, _: &dyn World, _: StyleChain) -> TypResult<Content> {
|
fn realize(&self, _: &dyn World, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.body.clone().unwrap_or_else(|| match &self.dest {
|
Ok(self.body.clone().unwrap_or_else(|| match &self.dest {
|
||||||
Destination::Url(url) => {
|
Destination::Url(url) => {
|
||||||
let mut text = url.as_str();
|
let mut text = url.as_str();
|
||||||
@ -83,7 +83,7 @@ impl Show for LinkNode {
|
|||||||
_: &dyn World,
|
_: &dyn World,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
mut realized: Content,
|
mut realized: Content,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
let mut map = StyleMap::new();
|
let mut map = StyleMap::new();
|
||||||
map.set(TextNode::LINK, Some(self.dest.clone()));
|
map.set(TextNode::LINK, Some(self.dest.clone()));
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ impl TextNode {
|
|||||||
#[property(skip, fold)]
|
#[property(skip, fold)]
|
||||||
pub const DECO: Decoration = vec![];
|
pub const DECO: Decoration = vec![];
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
// The text constructor is special: It doesn't create a text node.
|
// The text constructor is special: It doesn't create a text node.
|
||||||
// Instead, it leaves the passed argument structurally unchanged, but
|
// Instead, it leaves the passed argument structurally unchanged, but
|
||||||
// styles all text in it.
|
// styles all text in it.
|
||||||
@ -422,17 +422,17 @@ impl Fold for Vec<(Tag, u32)> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a string or content to lowercase.
|
/// Convert a string or content to lowercase.
|
||||||
pub fn lower(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn lower(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
case(Case::Lower, args)
|
case(Case::Lower, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a string or content to uppercase.
|
/// Convert a string or content to uppercase.
|
||||||
pub fn upper(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn upper(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
case(Case::Upper, args)
|
case(Case::Upper, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the case of text.
|
/// Change the case of text.
|
||||||
fn case(case: Case, args: &mut Args) -> TypResult<Value> {
|
fn case(case: Case, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v, span } = args.expect("string or content")?;
|
let Spanned { v, span } = args.expect("string or content")?;
|
||||||
Ok(match v {
|
Ok(match v {
|
||||||
Value::Str(v) => Value::Str(case.apply(&v).into()),
|
Value::Str(v) => Value::Str(case.apply(&v).into()),
|
||||||
@ -461,7 +461,7 @@ impl Case {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Display text in small capitals.
|
/// Display text in small capitals.
|
||||||
pub fn smallcaps(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn smallcaps(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let body: Content = args.expect("content")?;
|
let body: Content = args.expect("content")?;
|
||||||
Ok(Value::Content(body.styled(TextNode::SMALLCAPS, true)))
|
Ok(Value::Content(body.styled(TextNode::SMALLCAPS, true)))
|
||||||
}
|
}
|
||||||
@ -493,7 +493,7 @@ pub struct StrongNode(pub Content);
|
|||||||
|
|
||||||
#[node(showable)]
|
#[node(showable)]
|
||||||
impl StrongNode {
|
impl StrongNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show(Self(args.expect("body")?)))
|
Ok(Content::show(Self(args.expect("body")?)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -507,7 +507,7 @@ impl Show for StrongNode {
|
|||||||
dict! { "body" => Value::Content(self.0.clone()) }
|
dict! { "body" => Value::Content(self.0.clone()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, _: &dyn World, _: StyleChain) -> TypResult<Content> {
|
fn realize(&self, _: &dyn World, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.0.clone().styled(TextNode::BOLD, Toggle))
|
Ok(self.0.clone().styled(TextNode::BOLD, Toggle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -518,7 +518,7 @@ pub struct EmphNode(pub Content);
|
|||||||
|
|
||||||
#[node(showable)]
|
#[node(showable)]
|
||||||
impl EmphNode {
|
impl EmphNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show(Self(args.expect("body")?)))
|
Ok(Content::show(Self(args.expect("body")?)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -532,7 +532,7 @@ impl Show for EmphNode {
|
|||||||
dict! { "body" => Value::Content(self.0.clone()) }
|
dict! { "body" => Value::Content(self.0.clone()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, _: &dyn World, _: StyleChain) -> TypResult<Content> {
|
fn realize(&self, _: &dyn World, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.0.clone().styled(TextNode::ITALIC, Toggle))
|
Ok(self.0.clone().styled(TextNode::ITALIC, Toggle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ impl ParNode {
|
|||||||
#[property(resolve)]
|
#[property(resolve)]
|
||||||
pub const LINEBREAKS: Smart<Linebreaks> = Smart::Auto;
|
pub const LINEBREAKS: Smart<Linebreaks> = Smart::Auto;
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
// The paragraph constructor is special: It doesn't create a paragraph
|
// The paragraph constructor is special: It doesn't create a paragraph
|
||||||
// node. Instead, it just ensures that the passed content lives is in a
|
// node. Instead, it just ensures that the passed content lives is in a
|
||||||
// separate paragraph and styles it.
|
// separate paragraph and styles it.
|
||||||
@ -67,7 +67,7 @@ impl Layout for ParNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// Collect all text into one string for BiDi analysis.
|
// Collect all text into one string for BiDi analysis.
|
||||||
let (text, segments) = collect(self, &styles);
|
let (text, segments) = collect(self, &styles);
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ pub struct ParbreakNode;
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl ParbreakNode {
|
impl ParbreakNode {
|
||||||
fn construct(_: &mut Vm, _: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, _: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::Parbreak)
|
Ok(Content::Parbreak)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ pub struct LinebreakNode;
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl LinebreakNode {
|
impl LinebreakNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let justified = args.named("justified")?.unwrap_or(false);
|
let justified = args.named("justified")?.unwrap_or(false);
|
||||||
Ok(Content::Linebreak { justified })
|
Ok(Content::Linebreak { justified })
|
||||||
}
|
}
|
||||||
@ -502,7 +502,7 @@ fn prepare<'a>(
|
|||||||
segments: Vec<(Segment<'a>, StyleChain<'a>)>,
|
segments: Vec<(Segment<'a>, StyleChain<'a>)>,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> TypResult<Preparation<'a>> {
|
) -> SourceResult<Preparation<'a>> {
|
||||||
let bidi = BidiInfo::new(&text, match styles.get(TextNode::DIR) {
|
let bidi = BidiInfo::new(&text, match styles.get(TextNode::DIR) {
|
||||||
Dir::LTR => Some(Level::ltr()),
|
Dir::LTR => Some(Level::ltr()),
|
||||||
Dir::RTL => Some(Level::rtl()),
|
Dir::RTL => Some(Level::rtl()),
|
||||||
@ -1025,7 +1025,7 @@ fn stack(
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
lines: &[Line],
|
lines: &[Line],
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// Determine the paragraph's width: Full width of the region if we
|
// Determine the paragraph's width: Full width of the region if we
|
||||||
// should expand or there's fractional spacing, fit-to-width otherwise.
|
// should expand or there's fractional spacing, fit-to-width otherwise.
|
||||||
let mut width = regions.first.x;
|
let mut width = regions.first.x;
|
||||||
@ -1076,7 +1076,7 @@ fn commit(
|
|||||||
line: &Line,
|
line: &Line,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
width: Length,
|
width: Length,
|
||||||
) -> TypResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
let mut remaining = width - line.width;
|
let mut remaining = width - line.width;
|
||||||
let mut offset = Length::zero();
|
let mut offset = Length::zero();
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ impl RawNode {
|
|||||||
#[property(resolve, shorthand(around))]
|
#[property(resolve, shorthand(around))]
|
||||||
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
|
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show(Self {
|
Ok(Content::show(Self {
|
||||||
text: args.expect("text")?,
|
text: args.expect("text")?,
|
||||||
block: args.named("block")?.unwrap_or(false),
|
block: args.named("block")?.unwrap_or(false),
|
||||||
@ -59,7 +59,7 @@ impl Show for RawNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, _: &dyn World, styles: StyleChain) -> TypResult<Content> {
|
fn realize(&self, _: &dyn World, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase());
|
let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase());
|
||||||
let foreground = THEME
|
let foreground = THEME
|
||||||
.settings
|
.settings
|
||||||
@ -114,7 +114,7 @@ impl Show for RawNode {
|
|||||||
_: &dyn World,
|
_: &dyn World,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
mut realized: Content,
|
mut realized: Content,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
let mut map = StyleMap::new();
|
let mut map = StyleMap::new();
|
||||||
map.set_family(styles.get(Self::FAMILY).clone(), styles);
|
map.set_family(styles.get(Self::FAMILY).clone(), styles);
|
||||||
map.set(TextNode::OVERHANG, false);
|
map.set(TextNode::OVERHANG, false);
|
||||||
|
@ -6,7 +6,7 @@ pub struct RepeatNode(pub LayoutNode);
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl RepeatNode {
|
impl RepeatNode {
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::inline(Self(args.expect("body")?)))
|
Ok(Content::inline(Self(args.expect("body")?)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17,7 +17,7 @@ impl Layout for RepeatNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// The actual repeating happens directly in the paragraph.
|
// The actual repeating happens directly in the paragraph.
|
||||||
self.0.layout(world, regions, styles)
|
self.0.layout(world, regions, styles)
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ impl<'a> ShapedText<'a> {
|
|||||||
if let Some(font) = world
|
if let Some(font) = world
|
||||||
.book()
|
.book()
|
||||||
.select(family, self.variant)
|
.select(family, self.variant)
|
||||||
.and_then(|id| world.font(id).ok())
|
.and_then(|id| world.font(id))
|
||||||
{
|
{
|
||||||
expand(&font);
|
expand(&font);
|
||||||
break;
|
break;
|
||||||
@ -223,7 +223,7 @@ impl<'a> ShapedText<'a> {
|
|||||||
let font = world
|
let font = world
|
||||||
.book()
|
.book()
|
||||||
.select(family, self.variant)
|
.select(family, self.variant)
|
||||||
.and_then(|id| world.font(id).ok())?;
|
.and_then(|id| world.font(id))?;
|
||||||
let ttf = font.ttf();
|
let ttf = font.ttf();
|
||||||
let glyph_id = ttf.glyph_index('-')?;
|
let glyph_id = ttf.glyph_index('-')?;
|
||||||
let x_advance = font.to_em(ttf.glyph_hor_advance(glyph_id)?);
|
let x_advance = font.to_em(ttf.glyph_hor_advance(glyph_id)?);
|
||||||
@ -371,7 +371,7 @@ fn shape_segment<'a>(
|
|||||||
let book = ctx.world.book();
|
let book = ctx.world.book();
|
||||||
let mut selection = families.find_map(|family| {
|
let mut selection = families.find_map(|family| {
|
||||||
book.select(family, ctx.variant)
|
book.select(family, ctx.variant)
|
||||||
.and_then(|id| ctx.world.font(id).ok())
|
.and_then(|id| ctx.world.font(id))
|
||||||
.filter(|font| !ctx.used.contains(font))
|
.filter(|font| !ctx.used.contains(font))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -380,7 +380,7 @@ fn shape_segment<'a>(
|
|||||||
let first = ctx.used.first().map(Font::info);
|
let first = ctx.used.first().map(Font::info);
|
||||||
selection = book
|
selection = book
|
||||||
.select_fallback(first, ctx.variant, text)
|
.select_fallback(first, ctx.variant, text)
|
||||||
.and_then(|id| ctx.world.font(id).ok())
|
.and_then(|id| ctx.world.font(id))
|
||||||
.filter(|font| !ctx.used.contains(font));
|
.filter(|font| !ctx.used.contains(font));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ impl<const S: ScriptKind> ShiftNode<S> {
|
|||||||
/// The font size for synthetic sub- and superscripts.
|
/// The font size for synthetic sub- and superscripts.
|
||||||
pub const SIZE: TextSize = TextSize(Em::new(0.6).into());
|
pub const SIZE: TextSize = TextSize(Em::new(0.6).into());
|
||||||
|
|
||||||
fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Content::show(Self(args.expect("body")?)))
|
Ok(Content::show(Self(args.expect("body")?)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ impl<const S: ScriptKind> Show for ShiftNode<S> {
|
|||||||
dict! { "body" => Value::Content(self.0.clone()) }
|
dict! { "body" => Value::Content(self.0.clone()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, world: &dyn World, styles: StyleChain) -> TypResult<Content> {
|
fn realize(&self, world: &dyn World, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let mut transformed = None;
|
let mut transformed = None;
|
||||||
if styles.get(Self::TYPOGRAPHIC) {
|
if styles.get(Self::TYPOGRAPHIC) {
|
||||||
if let Some(text) = search_text(&self.0, S) {
|
if let Some(text) = search_text(&self.0, S) {
|
||||||
@ -96,7 +96,7 @@ fn is_shapable(world: &dyn World, text: &str, styles: StyleChain) -> bool {
|
|||||||
if let Some(font) = world
|
if let Some(font) = world
|
||||||
.book()
|
.book()
|
||||||
.select(family.as_str(), variant(styles))
|
.select(family.as_str(), variant(styles))
|
||||||
.and_then(|id| world.font(id).ok())
|
.and_then(|id| world.font(id))
|
||||||
{
|
{
|
||||||
return text.chars().all(|c| font.ttf().glyph_index(c).is_some());
|
return text.chars().all(|c| font.ttf().glyph_index(c).is_some());
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@ use std::str::FromStr;
|
|||||||
use crate::library::prelude::*;
|
use crate::library::prelude::*;
|
||||||
|
|
||||||
/// Create a grayscale color.
|
/// Create a grayscale color.
|
||||||
pub fn luma(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn luma(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Component(luma) = args.expect("gray component")?;
|
let Component(luma) = args.expect("gray component")?;
|
||||||
Ok(Value::Color(LumaColor::new(luma).into()))
|
Ok(Value::Color(LumaColor::new(luma).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an RGB(A) color.
|
/// Create an RGB(A) color.
|
||||||
pub fn rgb(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn rgb(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
Ok(Value::Color(
|
Ok(Value::Color(
|
||||||
if let Some(string) = args.find::<Spanned<EcoString>>()? {
|
if let Some(string) = args.find::<Spanned<EcoString>>()? {
|
||||||
match RgbaColor::from_str(&string.v) {
|
match RgbaColor::from_str(&string.v) {
|
||||||
@ -27,7 +27,7 @@ pub fn rgb(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a CMYK color.
|
/// Create a CMYK color.
|
||||||
pub fn cmyk(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn cmyk(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let RatioComponent(c) = args.expect("cyan component")?;
|
let RatioComponent(c) = args.expect("cyan component")?;
|
||||||
let RatioComponent(m) = args.expect("magenta component")?;
|
let RatioComponent(m) = args.expect("magenta component")?;
|
||||||
let RatioComponent(y) = args.expect("yellow component")?;
|
let RatioComponent(y) = args.expect("yellow component")?;
|
||||||
|
@ -1,30 +1,43 @@
|
|||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
use crate::library::prelude::*;
|
use crate::library::prelude::*;
|
||||||
|
|
||||||
/// Read structured data from a CSV file.
|
/// Read structured data from a CSV file.
|
||||||
pub fn csv(vm: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn csv(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v: path, span } =
|
let Spanned { v: path, span } =
|
||||||
args.expect::<Spanned<EcoString>>("path to csv file")?;
|
args.expect::<Spanned<EcoString>>("path to csv file")?;
|
||||||
|
|
||||||
let path = vm.locate(&path).at(span)?;
|
let path = vm.locate(&path).at(span)?;
|
||||||
let try_load = || -> io::Result<Value> {
|
let data = vm.world.file(&path).at(span)?;
|
||||||
let data = vm.world.file(&path)?;
|
|
||||||
|
|
||||||
let mut builder = csv::ReaderBuilder::new();
|
let mut builder = csv::ReaderBuilder::new();
|
||||||
builder.has_headers(false);
|
builder.has_headers(false);
|
||||||
|
|
||||||
let mut reader = builder.from_reader(data.as_slice());
|
let mut reader = builder.from_reader(data.as_slice());
|
||||||
let mut vec = vec![];
|
let mut vec = vec![];
|
||||||
|
|
||||||
for result in reader.records() {
|
for result in reader.records() {
|
||||||
vec.push(Value::Array(
|
let row = result.map_err(format_csv_error).at(span)?;
|
||||||
result?.iter().map(|field| Value::Str(field.into())).collect(),
|
let array = row.iter().map(|field| Value::Str(field.into())).collect();
|
||||||
))
|
vec.push(Value::Array(array))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Array(Array::from_vec(vec)))
|
Ok(Value::Array(Array::from_vec(vec)))
|
||||||
};
|
}
|
||||||
|
|
||||||
try_load()
|
/// Format the user-facing CSV error message.
|
||||||
.map_err(|err| failed_to_load("csv file", &path, err))
|
fn format_csv_error(error: csv::Error) -> String {
|
||||||
.at(span)
|
match error.kind() {
|
||||||
|
csv::ErrorKind::Utf8 { .. } => "file is not valid utf-8".into(),
|
||||||
|
csv::ErrorKind::UnequalLengths { pos, expected_len, len } => {
|
||||||
|
let mut msg = format!(
|
||||||
|
"failed to parse csv file: found {len} instead of {expected_len} fields"
|
||||||
|
);
|
||||||
|
if let Some(pos) = pos {
|
||||||
|
write!(msg, " in line {}", pos.line()).unwrap();
|
||||||
|
}
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
_ => "failed to parse csv file".into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use std::cmp::Ordering;
|
|||||||
use crate::library::prelude::*;
|
use crate::library::prelude::*;
|
||||||
|
|
||||||
/// Convert a value to an integer.
|
/// Convert a value to an integer.
|
||||||
pub fn int(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn int(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v, span } = args.expect("value")?;
|
let Spanned { v, span } = args.expect("value")?;
|
||||||
Ok(Value::Int(match v {
|
Ok(Value::Int(match v {
|
||||||
Value::Bool(v) => v as i64,
|
Value::Bool(v) => v as i64,
|
||||||
@ -18,7 +18,7 @@ pub fn int(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a value to a float.
|
/// Convert a value to a float.
|
||||||
pub fn float(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn float(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v, span } = args.expect("value")?;
|
let Spanned { v, span } = args.expect("value")?;
|
||||||
Ok(Value::Float(match v {
|
Ok(Value::Float(match v {
|
||||||
Value::Int(v) => v as f64,
|
Value::Int(v) => v as f64,
|
||||||
@ -32,7 +32,7 @@ pub fn float(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The absolute value of a numeric value.
|
/// The absolute value of a numeric value.
|
||||||
pub fn abs(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn abs(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v, span } = args.expect("numeric value")?;
|
let Spanned { v, span } = args.expect("numeric value")?;
|
||||||
Ok(match v {
|
Ok(match v {
|
||||||
Value::Int(v) => Value::Int(v.abs()),
|
Value::Int(v) => Value::Int(v.abs()),
|
||||||
@ -48,17 +48,17 @@ pub fn abs(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The minimum of a sequence of values.
|
/// The minimum of a sequence of values.
|
||||||
pub fn min(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn min(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
minmax(args, Ordering::Less)
|
minmax(args, Ordering::Less)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum of a sequence of values.
|
/// The maximum of a sequence of values.
|
||||||
pub fn max(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn max(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
minmax(args, Ordering::Greater)
|
minmax(args, Ordering::Greater)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the minimum or maximum of a sequence of values.
|
/// Find the minimum or maximum of a sequence of values.
|
||||||
fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> {
|
fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
|
||||||
let mut extremum = args.expect::<Value>("value")?;
|
let mut extremum = args.expect::<Value>("value")?;
|
||||||
for Spanned { v, span } in args.all::<Spanned<Value>>()? {
|
for Spanned { v, span } in args.all::<Spanned<Value>>()? {
|
||||||
match v.partial_cmp(&extremum) {
|
match v.partial_cmp(&extremum) {
|
||||||
@ -79,17 +79,17 @@ fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Whether an integer is even.
|
/// Whether an integer is even.
|
||||||
pub fn even(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn even(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 == 0))
|
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 == 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether an integer is odd.
|
/// Whether an integer is odd.
|
||||||
pub fn odd(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn odd(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 != 0))
|
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 != 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The modulo of two numbers.
|
/// The modulo of two numbers.
|
||||||
pub fn mod_(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn mod_(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v: v1, span: span1 } = args.expect("integer or float")?;
|
let Spanned { v: v1, span: span1 } = args.expect("integer or float")?;
|
||||||
let Spanned { v: v2, span: span2 } = args.expect("integer or float")?;
|
let Spanned { v: v2, span: span2 } = args.expect("integer or float")?;
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ pub fn mod_(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a sequence of numbers.
|
/// Create a sequence of numbers.
|
||||||
pub fn range(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn range(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let first = args.expect::<i64>("end")?;
|
let first = args.expect::<i64>("end")?;
|
||||||
let (start, end) = match args.eat::<i64>()? {
|
let (start, end) = match args.eat::<i64>()? {
|
||||||
Some(second) => (first, second),
|
Some(second) => (first, second),
|
||||||
|
@ -15,12 +15,12 @@ use crate::library::prelude::*;
|
|||||||
use crate::source::Source;
|
use crate::source::Source;
|
||||||
|
|
||||||
/// The name of a value's type.
|
/// The name of a value's type.
|
||||||
pub fn type_(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn type_(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
Ok(args.expect::<Value>("value")?.type_name().into())
|
Ok(args.expect::<Value>("value")?.type_name().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure that a condition is fulfilled.
|
/// Ensure that a condition is fulfilled.
|
||||||
pub fn assert(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn assert(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?;
|
let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?;
|
||||||
if !v {
|
if !v {
|
||||||
bail!(span, "assertion failed");
|
bail!(span, "assertion failed");
|
||||||
@ -29,7 +29,7 @@ pub fn assert(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a string as Typst markup.
|
/// Evaluate a string as Typst markup.
|
||||||
pub fn eval(vm: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn eval(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
|
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
|
||||||
|
|
||||||
// Parse the source and set a synthetic span for all nodes.
|
// Parse the source and set a synthetic span for all nodes.
|
||||||
@ -44,7 +44,7 @@ pub fn eval(vm: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
|||||||
|
|
||||||
// Handle control flow.
|
// Handle control flow.
|
||||||
if let Some(flow) = sub.flow {
|
if let Some(flow) = sub.flow {
|
||||||
return Err(flow.forbidden());
|
bail!(flow.forbidden());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Content(result?))
|
Ok(Value::Content(result?))
|
||||||
|
@ -2,12 +2,12 @@ use crate::eval::Regex;
|
|||||||
use crate::library::prelude::*;
|
use crate::library::prelude::*;
|
||||||
|
|
||||||
/// The string representation of a value.
|
/// The string representation of a value.
|
||||||
pub fn repr(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn repr(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
Ok(args.expect::<Value>("value")?.repr().into())
|
Ok(args.expect::<Value>("value")?.repr().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a value to a string.
|
/// Convert a value to a string.
|
||||||
pub fn str(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn str(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v, span } = args.expect("value")?;
|
let Spanned { v, span } = args.expect("value")?;
|
||||||
Ok(Value::Str(match v {
|
Ok(Value::Str(match v {
|
||||||
Value::Int(v) => format_str!("{}", v),
|
Value::Int(v) => format_str!("{}", v),
|
||||||
@ -18,33 +18,33 @@ pub fn str(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create blind text.
|
/// Create blind text.
|
||||||
pub fn lorem(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn lorem(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let words: usize = args.expect("number of words")?;
|
let words: usize = args.expect("number of words")?;
|
||||||
Ok(Value::Str(lipsum::lipsum(words).into()))
|
Ok(Value::Str(lipsum::lipsum(words).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a regular expression.
|
/// Create a regular expression.
|
||||||
pub fn regex(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn regex(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
|
let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
|
||||||
Ok(Regex::new(&v).at(span)?.into())
|
Ok(Regex::new(&v).at(span)?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an integer into one or multiple letters.
|
/// Converts an integer into one or multiple letters.
|
||||||
pub fn letter(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn letter(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
numbered(Numbering::Letter, args)
|
numbered(Numbering::Letter, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an integer into a roman numeral.
|
/// Converts an integer into a roman numeral.
|
||||||
pub fn roman(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn roman(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
numbered(Numbering::Roman, args)
|
numbered(Numbering::Roman, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a number into a symbol.
|
/// Convert a number into a symbol.
|
||||||
pub fn symbol(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
|
pub fn symbol(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
numbered(Numbering::Symbol, args)
|
numbered(Numbering::Symbol, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn numbered(numbering: Numbering, args: &mut Args) -> TypResult<Value> {
|
fn numbered(numbering: Numbering, args: &mut Args) -> SourceResult<Value> {
|
||||||
let n = args.expect::<usize>("non-negative integer")?;
|
let n = args.expect::<usize>("non-negative integer")?;
|
||||||
Ok(Value::Str(numbering.apply(n).into()))
|
Ok(Value::Str(numbering.apply(n).into()))
|
||||||
}
|
}
|
||||||
|
36
src/main.rs
36
src/main.rs
@ -17,7 +17,7 @@ use siphasher::sip128::{Hasher128, SipHasher};
|
|||||||
use termcolor::{ColorChoice, StandardStream, WriteColor};
|
use termcolor::{ColorChoice, StandardStream, WriteColor};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use typst::diag::{failed_to_load, Error, StrResult};
|
use typst::diag::{FileError, FileResult, SourceError, StrResult};
|
||||||
use typst::font::{Font, FontBook, FontInfo, FontVariant};
|
use typst::font::{Font, FontBook, FontInfo, FontVariant};
|
||||||
use typst::library::text::THEME;
|
use typst::library::text::THEME;
|
||||||
use typst::parse::TokenMode;
|
use typst::parse::TokenMode;
|
||||||
@ -209,9 +209,7 @@ fn typeset(command: TypesetCommand) -> StrResult<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the world that serves sources, fonts and files.
|
// Create the world that serves sources, fonts and files.
|
||||||
let id = world
|
let id = world.resolve(&command.input).map_err(|err| err.to_string())?;
|
||||||
.resolve(&command.input)
|
|
||||||
.map_err(|err| failed_to_load("source file", &command.input, err))?;
|
|
||||||
|
|
||||||
// Typeset.
|
// Typeset.
|
||||||
match typst::typeset(&world, id) {
|
match typst::typeset(&world, id) {
|
||||||
@ -234,7 +232,7 @@ fn typeset(command: TypesetCommand) -> StrResult<()> {
|
|||||||
/// Print diagnostic messages to the terminal.
|
/// Print diagnostic messages to the terminal.
|
||||||
fn print_diagnostics(
|
fn print_diagnostics(
|
||||||
world: &SystemWorld,
|
world: &SystemWorld,
|
||||||
errors: Vec<Error>,
|
errors: Vec<SourceError>,
|
||||||
) -> Result<(), codespan_reporting::files::Error> {
|
) -> Result<(), codespan_reporting::files::Error> {
|
||||||
let mut w = StandardStream::stderr(ColorChoice::Always);
|
let mut w = StandardStream::stderr(ColorChoice::Always);
|
||||||
let config = term::Config { tab_width: 2, ..Default::default() };
|
let config = term::Config { tab_width: 2, ..Default::default() };
|
||||||
@ -328,17 +326,13 @@ impl World for SystemWorld {
|
|||||||
&self.config
|
&self.config
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve(&self, path: &Path) -> io::Result<SourceId> {
|
fn resolve(&self, path: &Path) -> FileResult<SourceId> {
|
||||||
let hash = PathHash::new(path)?;
|
let hash = PathHash::new(path)?;
|
||||||
if let Some(&id) = self.nav.borrow().get(&hash) {
|
if let Some(&id) = self.nav.borrow().get(&hash) {
|
||||||
return Ok(id);
|
return Ok(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = fs::read(path)?;
|
let text = fs::read_to_string(path).map_err(|e| FileError::from_io(e, path))?;
|
||||||
let text = String::from_utf8(data).map_err(|_| {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidData, "file is not valid utf-8")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let id = SourceId::from_raw(self.sources.len() as u16);
|
let id = SourceId::from_raw(self.sources.len() as u16);
|
||||||
let source = Source::new(id, path, text);
|
let source = Source::new(id, path, text);
|
||||||
self.sources.push(Box::new(source));
|
self.sources.push(Box::new(source));
|
||||||
@ -355,7 +349,7 @@ impl World for SystemWorld {
|
|||||||
&self.book
|
&self.book
|
||||||
}
|
}
|
||||||
|
|
||||||
fn font(&self, id: usize) -> io::Result<Font> {
|
fn font(&self, id: usize) -> Option<Font> {
|
||||||
let slot = &self.fonts[id];
|
let slot = &self.fonts[id];
|
||||||
slot.font
|
slot.font
|
||||||
.get_or_init(|| {
|
.get_or_init(|| {
|
||||||
@ -363,14 +357,15 @@ impl World for SystemWorld {
|
|||||||
Font::new(data, slot.index)
|
Font::new(data, slot.index)
|
||||||
})
|
})
|
||||||
.clone()
|
.clone()
|
||||||
.ok_or_else(|| io::ErrorKind::InvalidData.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file(&self, path: &Path) -> io::Result<Buffer> {
|
fn file(&self, path: &Path) -> FileResult<Buffer> {
|
||||||
let hash = PathHash::new(path)?;
|
let hash = PathHash::new(path)?;
|
||||||
Ok(match self.files.borrow_mut().entry(hash) {
|
Ok(match self.files.borrow_mut().entry(hash) {
|
||||||
Entry::Occupied(entry) => entry.get().clone(),
|
Entry::Occupied(entry) => entry.get().clone(),
|
||||||
Entry::Vacant(entry) => entry.insert(fs::read(path)?.into()).clone(),
|
Entry::Vacant(entry) => entry
|
||||||
|
.insert(fs::read(path).map_err(|e| FileError::from_io(e, path))?.into())
|
||||||
|
.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,15 +375,16 @@ impl World for SystemWorld {
|
|||||||
struct PathHash(u128);
|
struct PathHash(u128);
|
||||||
|
|
||||||
impl PathHash {
|
impl PathHash {
|
||||||
fn new(path: &Path) -> io::Result<Self> {
|
fn new(path: &Path) -> FileResult<Self> {
|
||||||
let file = File::open(path)?;
|
let f = |e| FileError::from_io(e, path);
|
||||||
if file.metadata()?.is_file() {
|
let file = File::open(path).map_err(f)?;
|
||||||
let handle = Handle::from_file(file)?;
|
if file.metadata().map_err(f)?.is_file() {
|
||||||
|
let handle = Handle::from_file(file).map_err(f)?;
|
||||||
let mut state = SipHasher::new();
|
let mut state = SipHasher::new();
|
||||||
handle.hash(&mut state);
|
handle.hash(&mut state);
|
||||||
Ok(Self(state.finish128().as_u128()))
|
Ok(Self(state.finish128().as_u128()))
|
||||||
} else {
|
} else {
|
||||||
Err(io::ErrorKind::NotFound.into())
|
Err(FileError::NotFound(path.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ use crate::World;
|
|||||||
/// Layout content into a collection of pages.
|
/// Layout content into a collection of pages.
|
||||||
///
|
///
|
||||||
/// Relayouts until all pinned locations are converged.
|
/// Relayouts until all pinned locations are converged.
|
||||||
pub fn layout(world: &dyn World, content: &Content) -> TypResult<Vec<Frame>> {
|
pub fn layout(world: &dyn World, content: &Content) -> SourceResult<Vec<Frame>> {
|
||||||
let styles = StyleChain::with_root(&world.config().styles);
|
let styles = StyleChain::with_root(&world.config().styles);
|
||||||
let scratch = Scratch::default();
|
let scratch = Scratch::default();
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ impl Layout for Content {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let scratch = Scratch::default();
|
let scratch = Scratch::default();
|
||||||
let mut builder = Builder::new(world, &scratch, false);
|
let mut builder = Builder::new(world, &scratch, false);
|
||||||
builder.accept(self, styles)?;
|
builder.accept(self, styles)?;
|
||||||
@ -369,7 +369,7 @@ impl<'a, 'w> Builder<'a, 'w> {
|
|||||||
fn into_doc(
|
fn into_doc(
|
||||||
mut self,
|
mut self,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> TypResult<(DocNode, StyleChain<'a>)> {
|
) -> SourceResult<(DocNode, StyleChain<'a>)> {
|
||||||
self.interrupt(Interruption::Page, styles, true)?;
|
self.interrupt(Interruption::Page, styles, true)?;
|
||||||
let (pages, shared) = self.doc.unwrap().pages.finish();
|
let (pages, shared) = self.doc.unwrap().pages.finish();
|
||||||
Ok((DocNode(pages), shared))
|
Ok((DocNode(pages), shared))
|
||||||
@ -378,13 +378,17 @@ impl<'a, 'w> Builder<'a, 'w> {
|
|||||||
fn into_flow(
|
fn into_flow(
|
||||||
mut self,
|
mut self,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> TypResult<(FlowNode, StyleChain<'a>)> {
|
) -> SourceResult<(FlowNode, StyleChain<'a>)> {
|
||||||
self.interrupt(Interruption::Par, styles, false)?;
|
self.interrupt(Interruption::Par, styles, false)?;
|
||||||
let (children, shared) = self.flow.0.finish();
|
let (children, shared) = self.flow.0.finish();
|
||||||
Ok((FlowNode(children), shared))
|
Ok((FlowNode(children), shared))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> TypResult<()> {
|
fn accept(
|
||||||
|
&mut self,
|
||||||
|
content: &'a Content,
|
||||||
|
styles: StyleChain<'a>,
|
||||||
|
) -> SourceResult<()> {
|
||||||
match content {
|
match content {
|
||||||
Content::Empty => return Ok(()),
|
Content::Empty => return Ok(()),
|
||||||
Content::Text(text) => {
|
Content::Text(text) => {
|
||||||
@ -430,7 +434,7 @@ impl<'a, 'w> Builder<'a, 'w> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, node: &ShowNode, styles: StyleChain<'a>) -> TypResult<()> {
|
fn show(&mut self, node: &ShowNode, styles: StyleChain<'a>) -> SourceResult<()> {
|
||||||
if let Some(mut realized) = styles.apply(self.world, Target::Node(node))? {
|
if let Some(mut realized) = styles.apply(self.world, Target::Node(node))? {
|
||||||
let mut map = StyleMap::new();
|
let mut map = StyleMap::new();
|
||||||
let barrier = Barrier::new(node.id());
|
let barrier = Barrier::new(node.id());
|
||||||
@ -447,7 +451,7 @@ impl<'a, 'w> Builder<'a, 'w> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
(content, map): &'a (Content, StyleMap),
|
(content, map): &'a (Content, StyleMap),
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> TypResult<()> {
|
) -> SourceResult<()> {
|
||||||
let stored = self.scratch.styles.alloc(styles);
|
let stored = self.scratch.styles.alloc(styles);
|
||||||
let styles = map.chain(stored);
|
let styles = map.chain(stored);
|
||||||
let intr = map.interruption();
|
let intr = map.interruption();
|
||||||
@ -470,7 +474,7 @@ impl<'a, 'w> Builder<'a, 'w> {
|
|||||||
intr: Interruption,
|
intr: Interruption,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
keep: bool,
|
keep: bool,
|
||||||
) -> TypResult<()> {
|
) -> SourceResult<()> {
|
||||||
if intr >= Interruption::List && !self.list.is_empty() {
|
if intr >= Interruption::List && !self.list.is_empty() {
|
||||||
mem::take(&mut self.list).finish(self)?;
|
mem::take(&mut self.list).finish(self)?;
|
||||||
}
|
}
|
||||||
@ -493,7 +497,11 @@ impl<'a, 'w> Builder<'a, 'w> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sequence(&mut self, seq: &'a [Content], styles: StyleChain<'a>) -> TypResult<()> {
|
fn sequence(
|
||||||
|
&mut self,
|
||||||
|
seq: &'a [Content],
|
||||||
|
styles: StyleChain<'a>,
|
||||||
|
) -> SourceResult<()> {
|
||||||
for content in seq {
|
for content in seq {
|
||||||
self.accept(content, styles)?;
|
self.accept(content, styles)?;
|
||||||
}
|
}
|
||||||
@ -738,7 +746,7 @@ impl<'a> ListBuilder<'a> {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self, parent: &mut Builder<'a, '_>) -> TypResult<()> {
|
fn finish(self, parent: &mut Builder<'a, '_>) -> SourceResult<()> {
|
||||||
let (items, shared) = self.items.finish();
|
let (items, shared) = self.items.finish();
|
||||||
let kind = match items.items().next() {
|
let kind = match items.items().next() {
|
||||||
Some(item) => item.kind,
|
Some(item) => item.kind,
|
||||||
|
@ -6,7 +6,7 @@ use std::hash::Hash;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{Barrier, NodeId, Resolve, StyleChain, StyleEntry};
|
use super::{Barrier, NodeId, Resolve, StyleChain, StyleEntry};
|
||||||
use crate::diag::TypResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::eval::{RawAlign, RawLength};
|
use crate::eval::{RawAlign, RawLength};
|
||||||
use crate::frame::{Element, Frame};
|
use crate::frame::{Element, Frame};
|
||||||
use crate::geom::{
|
use crate::geom::{
|
||||||
@ -27,7 +27,7 @@ pub trait Layout: 'static {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>>;
|
) -> SourceResult<Vec<Frame>>;
|
||||||
|
|
||||||
/// Convert to a packed node.
|
/// Convert to a packed node.
|
||||||
fn pack(self) -> LayoutNode
|
fn pack(self) -> LayoutNode
|
||||||
@ -219,7 +219,7 @@ impl Layout for LayoutNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let barrier = StyleEntry::Barrier(Barrier::new(self.id()));
|
let barrier = StyleEntry::Barrier(Barrier::new(self.id()));
|
||||||
let styles = barrier.chain(&styles);
|
let styles = barrier.chain(&styles);
|
||||||
|
|
||||||
@ -288,7 +288,7 @@ impl Layout for EmptyNode {
|
|||||||
_: &dyn World,
|
_: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
_: StyleChain,
|
_: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
Ok(vec![Frame::new(
|
Ok(vec![Frame::new(
|
||||||
regions.expand.select(regions.first, Size::zero()),
|
regions.expand.select(regions.first, Size::zero()),
|
||||||
)])
|
)])
|
||||||
@ -310,7 +310,7 @@ impl Layout for SizedNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
// The "pod" is the region into which the child will be layouted.
|
// The "pod" is the region into which the child will be layouted.
|
||||||
let pod = {
|
let pod = {
|
||||||
// Resolve the sizing to a concrete size.
|
// Resolve the sizing to a concrete size.
|
||||||
@ -357,7 +357,7 @@ impl Layout for FillNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let mut frames = self.child.layout(world, regions, styles)?;
|
let mut frames = self.child.layout(world, regions, styles)?;
|
||||||
for frame in &mut frames {
|
for frame in &mut frames {
|
||||||
let shape = Geometry::Rect(frame.size()).filled(self.fill);
|
let shape = Geometry::Rect(frame.size()).filled(self.fill);
|
||||||
@ -382,7 +382,7 @@ impl Layout for StrokeNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> TypResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let mut frames = self.child.layout(world, regions, styles)?;
|
let mut frames = self.child.layout(world, regions, styles)?;
|
||||||
for frame in &mut frames {
|
for frame in &mut frames {
|
||||||
let shape = Geometry::Rect(frame.size()).stroked(self.stroke);
|
let shape = Geometry::Rect(frame.size()).stroked(self.stroke);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
use super::{Content, Interruption, NodeId, Show, ShowNode, StyleChain, StyleEntry};
|
use super::{Content, Interruption, NodeId, Show, ShowNode, StyleChain, StyleEntry};
|
||||||
use crate::diag::TypResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::eval::{Args, Func, Regex, Value};
|
use crate::eval::{Args, Func, Regex, Value};
|
||||||
use crate::library::structure::{EnumNode, ListNode};
|
use crate::library::structure::{EnumNode, ListNode};
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::Spanned;
|
||||||
@ -33,7 +33,7 @@ impl Recipe {
|
|||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
sel: Selector,
|
sel: Selector,
|
||||||
target: Target,
|
target: Target,
|
||||||
) -> TypResult<Option<Content>> {
|
) -> SourceResult<Option<Content>> {
|
||||||
let content = match (target, &self.pattern) {
|
let content = match (target, &self.pattern) {
|
||||||
(Target::Node(node), &Pattern::Node(id)) if node.id() == id => {
|
(Target::Node(node), &Pattern::Node(id)) if node.id() == id => {
|
||||||
let node = node.unguard(sel);
|
let node = node.unguard(sel);
|
||||||
@ -75,7 +75,7 @@ impl Recipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call the recipe function, with the argument if desired.
|
/// Call the recipe function, with the argument if desired.
|
||||||
fn call<F>(&self, world: &dyn World, arg: F) -> TypResult<Content>
|
fn call<F>(&self, world: &dyn World, arg: F) -> SourceResult<Content>
|
||||||
where
|
where
|
||||||
F: FnOnce() -> Value,
|
F: FnOnce() -> Value,
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,7 @@ use std::hash::Hash;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{Content, NodeId, Selector, StyleChain};
|
use super::{Content, NodeId, Selector, StyleChain};
|
||||||
use crate::diag::TypResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::eval::Dict;
|
use crate::eval::Dict;
|
||||||
use crate::util::Prehashed;
|
use crate::util::Prehashed;
|
||||||
use crate::World;
|
use crate::World;
|
||||||
@ -18,7 +18,7 @@ pub trait Show: 'static {
|
|||||||
|
|
||||||
/// The base recipe for this node that is executed if there is no
|
/// The base recipe for this node that is executed if there is no
|
||||||
/// user-defined show rule.
|
/// user-defined show rule.
|
||||||
fn realize(&self, world: &dyn World, styles: StyleChain) -> TypResult<Content>;
|
fn realize(&self, world: &dyn World, styles: StyleChain) -> SourceResult<Content>;
|
||||||
|
|
||||||
/// Finalize this node given the realization of a base or user recipe. Use
|
/// Finalize this node given the realization of a base or user recipe. Use
|
||||||
/// this for effects that should work even in the face of a user-defined
|
/// this for effects that should work even in the face of a user-defined
|
||||||
@ -33,7 +33,7 @@ pub trait Show: 'static {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
realized: Content,
|
realized: Content,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
Ok(realized)
|
Ok(realized)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ impl Show for ShowNode {
|
|||||||
self.0.encode(styles)
|
self.0.encode(styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realize(&self, world: &dyn World, styles: StyleChain) -> TypResult<Content> {
|
fn realize(&self, world: &dyn World, styles: StyleChain) -> SourceResult<Content> {
|
||||||
self.0.realize(world, styles)
|
self.0.realize(world, styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ impl Show for ShowNode {
|
|||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
realized: Content,
|
realized: Content,
|
||||||
) -> TypResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
self.0.finalize(world, styles, realized)
|
self.0.finalize(world, styles, realized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use std::iter;
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use super::{Barrier, Content, Key, Property, Recipe, Selector, Show, Target};
|
use super::{Barrier, Content, Key, Property, Recipe, Selector, Show, Target};
|
||||||
use crate::diag::TypResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::frame::Role;
|
use crate::frame::Role;
|
||||||
use crate::library::text::{FontFamily, TextNode};
|
use crate::library::text::{FontFamily, TextNode};
|
||||||
use crate::util::ReadableTypeId;
|
use crate::util::ReadableTypeId;
|
||||||
@ -277,7 +277,11 @@ impl<'a> StyleChain<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Apply show recipes in this style chain to a target.
|
/// Apply show recipes in this style chain to a target.
|
||||||
pub fn apply(self, world: &dyn World, target: Target) -> TypResult<Option<Content>> {
|
pub fn apply(
|
||||||
|
self,
|
||||||
|
world: &dyn World,
|
||||||
|
target: Target,
|
||||||
|
) -> SourceResult<Option<Content>> {
|
||||||
// Find out how many recipes there any and whether any of their patterns
|
// Find out how many recipes there any and whether any of their patterns
|
||||||
// match.
|
// match.
|
||||||
let mut n = 0;
|
let mut n = 0;
|
||||||
|
@ -5,13 +5,13 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
|
|
||||||
use crate::diag::TypResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::parse::{is_newline, parse, reparse};
|
use crate::parse::{is_newline, parse, reparse};
|
||||||
use crate::syntax::ast::Markup;
|
use crate::syntax::ast::Markup;
|
||||||
use crate::syntax::{Span, SyntaxNode};
|
use crate::syntax::{Span, SyntaxNode};
|
||||||
use crate::util::{PathExt, StrExt};
|
use crate::util::{PathExt, StrExt};
|
||||||
|
|
||||||
/// A single source file.
|
/// A source file.
|
||||||
///
|
///
|
||||||
/// _Note_: All line and column indices start at zero, just like byte indices.
|
/// _Note_: All line and column indices start at zero, just like byte indices.
|
||||||
/// Only for user-facing display, you should add 1 to them.
|
/// Only for user-facing display, you should add 1 to them.
|
||||||
@ -63,10 +63,10 @@ impl Source {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The root node of the file's typed abstract syntax tree.
|
/// The root node of the file's typed abstract syntax tree.
|
||||||
pub fn ast(&self) -> TypResult<Markup> {
|
pub fn ast(&self) -> SourceResult<Markup> {
|
||||||
let errors = self.root.errors();
|
let errors = self.root.errors();
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
Ok(self.root.cast().unwrap())
|
Ok(self.root.cast().expect("root node must be markup"))
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(errors))
|
Err(Box::new(errors))
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ pub use highlight::*;
|
|||||||
pub use span::*;
|
pub use span::*;
|
||||||
|
|
||||||
use self::ast::{MathNode, RawNode, TypedNode, Unit};
|
use self::ast::{MathNode, RawNode, TypedNode, Unit};
|
||||||
use crate::diag::Error;
|
use crate::diag::SourceError;
|
||||||
use crate::source::SourceId;
|
use crate::source::SourceId;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
@ -67,14 +67,14 @@ impl SyntaxNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The error messages for this node and its descendants.
|
/// The error messages for this node and its descendants.
|
||||||
pub fn errors(&self) -> Vec<Error> {
|
pub fn errors(&self) -> Vec<SourceError> {
|
||||||
if !self.erroneous() {
|
if !self.erroneous() {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
&NodeKind::Error(pos, ref message) => {
|
&NodeKind::Error(pos, ref message) => {
|
||||||
vec![Error::new(self.span().with_pos(pos), message)]
|
vec![SourceError::new(self.span().with_pos(pos), message)]
|
||||||
}
|
}
|
||||||
_ => self
|
_ => self
|
||||||
.children()
|
.children()
|
||||||
|
5
tests/res/bad.svg
Normal file
5
tests/res/bad.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||||
|
<style>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 118 B |
@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Some non-text stuff.
|
// Some non-text stuff.
|
||||||
// Error: 16-37 failed to load source file (file is not valid utf-8)
|
// Error: 16-37 file is not valid utf-8
|
||||||
#import * from "../../res/rhino.png"
|
#import * from "../../res/rhino.png"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -55,5 +55,9 @@ A #image("/res/tiger.jpg", height: 1cm, width: 80%) B
|
|||||||
#image("path/does/not/exist")
|
#image("path/does/not/exist")
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 8-21 failed to load image (unknown image format)
|
// Error: 8-21 unknown image format
|
||||||
#image("./image.typ")
|
#image("./image.typ")
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 8-22 failed to parse svg: found closing tag 'g' instead of 'style' in line 4
|
||||||
|
#image("/res/bad.svg")
|
||||||
|
@ -11,5 +11,5 @@
|
|||||||
#csv("nope.csv")
|
#csv("nope.csv")
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 6-20 failed to load csv file (CSV error: record 2 (line: 3, byte: 8): found record with 3 fields, but the previous record has 2 fields)
|
// Error: 6-20 failed to parse csv file: found 3 instead of 2 fields in line 3
|
||||||
#csv("/res/bad.csv")
|
#csv("/res/bad.csv")
|
||||||
|
@ -4,7 +4,6 @@ use std::env;
|
|||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::io;
|
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
@ -15,6 +14,7 @@ use tiny_skia as sk;
|
|||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
use typst::diag::{FileError, FileResult};
|
||||||
use typst::eval::{Smart, Value};
|
use typst::eval::{Smart, Value};
|
||||||
use typst::font::{Font, FontBook};
|
use typst::font::{Font, FontBook};
|
||||||
use typst::frame::{Element, Frame};
|
use typst::frame::{Element, Frame};
|
||||||
@ -238,17 +238,13 @@ impl World for TestWorld {
|
|||||||
&self.config
|
&self.config
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve(&self, path: &Path) -> io::Result<SourceId> {
|
fn resolve(&self, path: &Path) -> FileResult<SourceId> {
|
||||||
let hash = PathHash::new(path)?;
|
let hash = PathHash::new(path)?;
|
||||||
if let Some(&id) = self.nav.borrow().get(&hash) {
|
if let Some(&id) = self.nav.borrow().get(&hash) {
|
||||||
return Ok(id);
|
return Ok(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = fs::read(path)?;
|
let text = fs::read_to_string(path).map_err(|e| FileError::from_io(e, path))?;
|
||||||
let text = String::from_utf8(data).map_err(|_| {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidData, "file is not valid utf-8")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let id = SourceId::from_raw(self.sources.len() as u16);
|
let id = SourceId::from_raw(self.sources.len() as u16);
|
||||||
let source = Source::new(id, path, text);
|
let source = Source::new(id, path, text);
|
||||||
self.sources.push(Box::new(source));
|
self.sources.push(Box::new(source));
|
||||||
@ -265,15 +261,17 @@ impl World for TestWorld {
|
|||||||
&self.book
|
&self.book
|
||||||
}
|
}
|
||||||
|
|
||||||
fn font(&self, id: usize) -> io::Result<Font> {
|
fn font(&self, id: usize) -> Option<Font> {
|
||||||
Ok(self.fonts[id].clone())
|
Some(self.fonts[id].clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file(&self, path: &Path) -> io::Result<Buffer> {
|
fn file(&self, path: &Path) -> FileResult<Buffer> {
|
||||||
let hash = PathHash::new(path)?;
|
let hash = PathHash::new(path)?;
|
||||||
Ok(match self.files.borrow_mut().entry(hash) {
|
Ok(match self.files.borrow_mut().entry(hash) {
|
||||||
Entry::Occupied(entry) => entry.get().clone(),
|
Entry::Occupied(entry) => entry.get().clone(),
|
||||||
Entry::Vacant(entry) => entry.insert(fs::read(path)?.into()).clone(),
|
Entry::Vacant(entry) => entry
|
||||||
|
.insert(fs::read(path).map_err(|e| FileError::from_io(e, path))?.into())
|
||||||
|
.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,15 +281,16 @@ impl World for TestWorld {
|
|||||||
struct PathHash(u128);
|
struct PathHash(u128);
|
||||||
|
|
||||||
impl PathHash {
|
impl PathHash {
|
||||||
fn new(path: &Path) -> io::Result<Self> {
|
fn new(path: &Path) -> FileResult<Self> {
|
||||||
let file = File::open(path)?;
|
let f = |e| FileError::from_io(e, path);
|
||||||
if file.metadata()?.is_file() {
|
let file = File::open(path).map_err(f)?;
|
||||||
let handle = Handle::from_file(file)?;
|
if file.metadata().map_err(f)?.is_file() {
|
||||||
|
let handle = Handle::from_file(file).map_err(f)?;
|
||||||
let mut state = SipHasher::new();
|
let mut state = SipHasher::new();
|
||||||
handle.hash(&mut state);
|
handle.hash(&mut state);
|
||||||
Ok(Self(state.finish128().as_u128()))
|
Ok(Self(state.finish128().as_u128()))
|
||||||
} else {
|
} else {
|
||||||
Err(io::ErrorKind::NotFound.into())
|
Err(FileError::NotFound(path.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user