mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Merge pull request #5 from typst/problems-and-error-macro
Rename errors to problems and make error! macro more ergonomic
This commit is contained in:
commit
e7ffdde43d
73
src/error.rs
73
src/error.rs
@ -1,73 +0,0 @@
|
|||||||
//! Errors in source code.
|
|
||||||
//!
|
|
||||||
//! There are no fatal errors in _Typst_. The document will always compile and
|
|
||||||
//! yield a layout. However, this is a best effort process and bad things will
|
|
||||||
//! still generate errors and warnings.
|
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
use crate::syntax::span::SpanVec;
|
|
||||||
|
|
||||||
|
|
||||||
/// A spanned list of errors.
|
|
||||||
pub type Errors = SpanVec<Error>;
|
|
||||||
|
|
||||||
/// An error that arose in parsing or layouting.
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
|
|
||||||
pub struct Error {
|
|
||||||
/// An error message describing the problem.
|
|
||||||
pub message: String,
|
|
||||||
/// How severe / important the error is.
|
|
||||||
pub severity: Severity,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// How severe / important an error is.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub enum Severity {
|
|
||||||
/// Something in the code is not good.
|
|
||||||
Warning,
|
|
||||||
/// Something in the code is wrong!
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error {
|
|
||||||
/// Create a new error from message and severity.
|
|
||||||
pub fn new(message: impl Into<String>, severity: Severity) -> Error {
|
|
||||||
Error { message: message.into(), severity }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct an error with formatted message and optionally severity and / or
|
|
||||||
/// span.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// # use typstc::err;
|
|
||||||
/// # use typstc::syntax::span::Span;
|
|
||||||
/// # let span = Span::ZERO;
|
|
||||||
/// # let value = 0;
|
|
||||||
///
|
|
||||||
/// // With span and default severity `Error`.
|
|
||||||
/// err!(span; "the wrong {}", value);
|
|
||||||
///
|
|
||||||
/// // With no span and severity `Warning`.
|
|
||||||
/// err!(@Warning: span; "non-fatal!");
|
|
||||||
///
|
|
||||||
/// // Without span and default severity.
|
|
||||||
/// err!("no spans here ...");
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! err {
|
|
||||||
(@$severity:ident: $span:expr; $($args:tt)*) => {
|
|
||||||
$crate::syntax::span::Spanned { v: err!(@$severity: $($args)*), span: $span }
|
|
||||||
};
|
|
||||||
|
|
||||||
(@$severity:ident: $($args:tt)*) => {
|
|
||||||
$crate::error::Error {
|
|
||||||
message: format!($($args)*),
|
|
||||||
severity: $crate::error::Severity::$severity,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
($($tts:tt)*) => { err!(@Error: $($tts)*) };
|
|
||||||
}
|
|
10
src/func.rs
10
src/func.rs
@ -7,7 +7,7 @@ use crate::syntax::span::Spanned;
|
|||||||
|
|
||||||
/// Types that are useful for creating your own functions.
|
/// Types that are useful for creating your own functions.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{function, body, err};
|
pub use crate::{function, body, error, warning};
|
||||||
pub use crate::layout::prelude::*;
|
pub use crate::layout::prelude::*;
|
||||||
pub use crate::layout::Command::{self, *};
|
pub use crate::layout::Command::{self, *};
|
||||||
pub use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
pub use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||||
@ -55,8 +55,8 @@ pub trait ParseFunc {
|
|||||||
///
|
///
|
||||||
/// parse(header, body, ctx, f) {
|
/// parse(header, body, ctx, f) {
|
||||||
/// let body = body!(opt: body, ctx, f);
|
/// let body = body!(opt: body, ctx, f);
|
||||||
/// let hidden = header.args.pos.get::<bool>(&mut f.errors)
|
/// let hidden = header.args.pos.get::<bool>(&mut f.problems)
|
||||||
/// .or_missing(&mut f.errors, header.name.span, "hidden")
|
/// .or_missing(&mut f.problems, header.name.span, "hidden")
|
||||||
/// .unwrap_or(false);
|
/// .unwrap_or(false);
|
||||||
///
|
///
|
||||||
/// HiderFunc { body: if hidden { None } else { body } }
|
/// HiderFunc { body: if hidden { None } else { body } }
|
||||||
@ -132,7 +132,7 @@ macro_rules! function {
|
|||||||
let func = $code;
|
let func = $code;
|
||||||
|
|
||||||
for arg in header.args.into_iter() {
|
for arg in header.args.into_iter() {
|
||||||
feedback.errors.push(err!(arg.span; "unexpected argument"));
|
error!(@feedback, arg.span, "unexpected argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
$crate::Pass::new(func, feedback)
|
$crate::Pass::new(func, feedback)
|
||||||
@ -189,7 +189,7 @@ macro_rules! body {
|
|||||||
|
|
||||||
(nope: $body:expr, $feedback:expr) => {
|
(nope: $body:expr, $feedback:expr) => {
|
||||||
if let Some(body) = $body {
|
if let Some(body) = $body {
|
||||||
$feedback.errors.push($crate::err!(body.span; "unexpected body"));
|
error!(@$feedback, body.span, "unexpected body");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -245,8 +245,10 @@ impl<'a> ModelLayouter<'a> {
|
|||||||
BreakParagraph => self.layout_paragraph(),
|
BreakParagraph => self.layout_paragraph(),
|
||||||
BreakPage => {
|
BreakPage => {
|
||||||
if self.ctx.nested {
|
if self.ctx.nested {
|
||||||
self.feedback.errors.push(err!(model_span;
|
error!(
|
||||||
"page break cannot be issued from nested context"));
|
@self.feedback, model_span,
|
||||||
|
"page break cannot be issued from nested context",
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
self.layouter.finish_space(true)
|
self.layouter.finish_space(true)
|
||||||
}
|
}
|
||||||
@ -258,8 +260,10 @@ impl<'a> ModelLayouter<'a> {
|
|||||||
}
|
}
|
||||||
SetPageStyle(style) => {
|
SetPageStyle(style) => {
|
||||||
if self.ctx.nested {
|
if self.ctx.nested {
|
||||||
self.feedback.errors.push(err!(model_span;
|
error!(
|
||||||
"page style cannot be changed from nested context"));
|
@self.feedback, model_span,
|
||||||
|
"page style cannot be changed from nested context",
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
self.style.page = style;
|
self.style.page = style;
|
||||||
|
|
||||||
|
14
src/lib.rs
14
src/lib.rs
@ -27,7 +27,7 @@ use toddle::{Font, OwnedData};
|
|||||||
use toddle::query::{FontLoader, SharedFontLoader};
|
use toddle::query::{FontLoader, SharedFontLoader};
|
||||||
use toddle::query::{FontProvider, FontIndex, FontDescriptor};
|
use toddle::query::{FontProvider, FontIndex, FontDescriptor};
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::problem::Problems;
|
||||||
use crate::layout::MultiLayout;
|
use crate::layout::MultiLayout;
|
||||||
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||||
use crate::syntax::{SyntaxModel, Scope, Decoration, ParseContext, parse};
|
use crate::syntax::{SyntaxModel, Scope, Decoration, ParseContext, parse};
|
||||||
@ -43,7 +43,7 @@ macro_rules! pub_use_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod error;
|
pub mod problem;
|
||||||
pub mod export;
|
pub mod export;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod func;
|
pub mod func;
|
||||||
@ -175,8 +175,8 @@ impl<T> Pass<T> {
|
|||||||
/// User feedback data accumulated during a compilation pass.
|
/// User feedback data accumulated during a compilation pass.
|
||||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||||
pub struct Feedback {
|
pub struct Feedback {
|
||||||
/// Errors in the source.
|
/// Problems in the source code.
|
||||||
pub errors: SpanVec<Error>,
|
pub problems: Problems,
|
||||||
/// Decorations of the source code for semantic syntax highlighting.
|
/// Decorations of the source code for semantic syntax highlighting.
|
||||||
pub decos: SpanVec<Decoration>,
|
pub decos: SpanVec<Decoration>,
|
||||||
}
|
}
|
||||||
@ -185,7 +185,7 @@ impl Feedback {
|
|||||||
/// Create a new feedback instance without errors and decos.
|
/// Create a new feedback instance without errors and decos.
|
||||||
pub fn new() -> Feedback {
|
pub fn new() -> Feedback {
|
||||||
Feedback {
|
Feedback {
|
||||||
errors: vec![],
|
problems: vec![],
|
||||||
decos: vec![],
|
decos: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,14 +198,14 @@ impl Feedback {
|
|||||||
|
|
||||||
/// Add other feedback data to this feedback.
|
/// Add other feedback data to this feedback.
|
||||||
pub fn extend(&mut self, other: Feedback) {
|
pub fn extend(&mut self, other: Feedback) {
|
||||||
self.errors.extend(other.errors);
|
self.problems.extend(other.problems);
|
||||||
self.decos.extend(other.decos);
|
self.decos.extend(other.decos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add more feedback whose spans are local and need to be offset by an
|
/// Add more feedback whose spans are local and need to be offset by an
|
||||||
/// `offset` to be correct for this feedbacks context.
|
/// `offset` to be correct for this feedbacks context.
|
||||||
pub fn extend_offset(&mut self, offset: Position, other: Feedback) {
|
pub fn extend_offset(&mut self, offset: Position, other: Feedback) {
|
||||||
self.errors.extend(offset_spans(offset, other.errors));
|
self.problems.extend(offset_spans(offset, other.problems));
|
||||||
self.decos.extend(offset_spans(offset, other.decos));
|
self.decos.extend(offset_spans(offset, other.decos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,17 +13,17 @@ function! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
let list = header.args.pos.get_all::<StringLike>(&mut f.errors)
|
let list = header.args.pos.get_all::<StringLike>(&mut f.problems)
|
||||||
.map(|s| s.0.to_lowercase())
|
.map(|s| s.0.to_lowercase())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let tuples: Vec<_> = header.args.key
|
let tuples: Vec<_> = header.args.key
|
||||||
.get_all::<String, Tuple>(&mut f.errors)
|
.get_all::<String, Tuple>(&mut f.problems)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let classes = tuples.into_iter()
|
let classes = tuples.into_iter()
|
||||||
.map(|(class, mut tuple)| {
|
.map(|(class, mut tuple)| {
|
||||||
let fallback = tuple.get_all::<StringLike>(&mut f.errors)
|
let fallback = tuple.get_all::<StringLike>(&mut f.problems)
|
||||||
.map(|s| s.0.to_lowercase())
|
.map(|s| s.0.to_lowercase())
|
||||||
.collect();
|
.collect();
|
||||||
(class.to_lowercase(), fallback)
|
(class.to_lowercase(), fallback)
|
||||||
@ -37,7 +37,7 @@ function! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
styled(&self.body, ctx, Some(()),
|
styled(&self.body, ctx, Some(()),
|
||||||
|s, _| {
|
|s, _| {
|
||||||
if !self.list.is_empty() {
|
if !self.list.is_empty() {
|
||||||
@ -64,12 +64,12 @@ function! {
|
|||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
FontStyleFunc {
|
FontStyleFunc {
|
||||||
body: body!(opt: body, ctx, f),
|
body: body!(opt: body, ctx, f),
|
||||||
style: header.args.pos.get::<FontStyle>(&mut f.errors)
|
style: header.args.pos.get::<FontStyle>(&mut f.problems)
|
||||||
.or_missing(&mut f.errors, header.name.span, "style"),
|
.or_missing(&mut f.problems, header.name.span, "style"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
styled(&self.body, ctx, self.style, |t, s| t.variant.style = s)
|
styled(&self.body, ctx, self.style, |t, s| t.variant.style = s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,22 +84,24 @@ function! {
|
|||||||
|
|
||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
let body = body!(opt: body, ctx, f);
|
let body = body!(opt: body, ctx, f);
|
||||||
let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.errors)
|
let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.problems)
|
||||||
.map(|Spanned { v: (weight, is_clamped), span }| {
|
.map(|Spanned { v: (weight, is_clamped), span }| {
|
||||||
if is_clamped {
|
if is_clamped {
|
||||||
f.errors.push(err!(@Warning: span;
|
warning!(
|
||||||
"weight should be between \
|
@f, span,
|
||||||
100 and 900, clamped to {}", weight.0));
|
"weight should be between 100 and 900, clamped to {}",
|
||||||
|
weight.0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
weight
|
weight
|
||||||
})
|
})
|
||||||
.or_missing(&mut f.errors, header.name.span, "weight");
|
.or_missing(&mut f.problems, header.name.span, "weight");
|
||||||
|
|
||||||
FontWeightFunc { body, weight }
|
FontWeightFunc { body, weight }
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
styled(&self.body, ctx, self.weight, |t, w| t.variant.weight = w)
|
styled(&self.body, ctx, self.weight, |t, w| t.variant.weight = w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,12 +117,12 @@ function! {
|
|||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
FontSizeFunc {
|
FontSizeFunc {
|
||||||
body: body!(opt: body, ctx, f),
|
body: body!(opt: body, ctx, f),
|
||||||
size: header.args.pos.get::<FSize>(&mut f.errors)
|
size: header.args.pos.get::<FSize>(&mut f.problems)
|
||||||
.or_missing(&mut f.errors, header.name.span, "size")
|
.or_missing(&mut f.problems, header.name.span, "size")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
styled(&self.body, ctx, self.size, |t, s| {
|
styled(&self.body, ctx, self.size, |t, s| {
|
||||||
match s {
|
match s {
|
||||||
FSize::Absolute(size) => {
|
FSize::Absolute(size) => {
|
||||||
|
@ -13,14 +13,14 @@ function! {
|
|||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
AlignFunc {
|
AlignFunc {
|
||||||
body: body!(opt: body, ctx, f),
|
body: body!(opt: body, ctx, f),
|
||||||
map: PosAxisMap::parse::<AxisKey>(&mut f.errors, &mut header.args),
|
map: PosAxisMap::parse::<AxisKey>(&mut f.problems, &mut header.args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, f) {
|
layout(self, ctx, f) {
|
||||||
ctx.base = ctx.spaces[0].dimensions;
|
ctx.base = ctx.spaces[0].dimensions;
|
||||||
|
|
||||||
let map = self.map.dedup(&mut f.errors, ctx.axes, |alignment| {
|
let map = self.map.dedup(&mut f.problems, ctx.axes, |alignment| {
|
||||||
alignment.axis().map(|s| s.to_generic(ctx.axes))
|
alignment.axis().map(|s| s.to_generic(ctx.axes))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -29,8 +29,10 @@ function! {
|
|||||||
if let Some(generic) = alignment.to_generic(ctx.axes, axis) {
|
if let Some(generic) = alignment.to_generic(ctx.axes, axis) {
|
||||||
*ctx.alignment.get_mut(axis) = generic;
|
*ctx.alignment.get_mut(axis) = generic;
|
||||||
} else {
|
} else {
|
||||||
f.errors.push(err!(span;
|
error!(
|
||||||
"invalid alignment `{}` for {} axis", alignment, axis));
|
@f, span,
|
||||||
|
"invalid alignment `{}` for {} axis", alignment, axis,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,14 +61,14 @@ function! {
|
|||||||
DirectionFunc {
|
DirectionFunc {
|
||||||
name_span: header.name.span,
|
name_span: header.name.span,
|
||||||
body: body!(opt: body, ctx, f),
|
body: body!(opt: body, ctx, f),
|
||||||
map: PosAxisMap::parse::<AxisKey>(&mut f.errors, &mut header.args),
|
map: PosAxisMap::parse::<AxisKey>(&mut f.problems, &mut header.args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, f) {
|
layout(self, ctx, f) {
|
||||||
ctx.base = ctx.spaces[0].dimensions;
|
ctx.base = ctx.spaces[0].dimensions;
|
||||||
|
|
||||||
let map = self.map.dedup(&mut f.errors, ctx.axes, |direction| {
|
let map = self.map.dedup(&mut f.problems, ctx.axes, |direction| {
|
||||||
Some(direction.axis().to_generic(ctx.axes))
|
Some(direction.axis().to_generic(ctx.axes))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -76,9 +78,11 @@ function! {
|
|||||||
map.with(Secondary, |&dir| axes.secondary = dir);
|
map.with(Secondary, |&dir| axes.secondary = dir);
|
||||||
|
|
||||||
if axes.primary.axis() == axes.secondary.axis() {
|
if axes.primary.axis() == axes.secondary.axis() {
|
||||||
f.errors.push(err!(self.name_span;
|
error!(
|
||||||
|
@f, self.name_span,
|
||||||
"invalid aligned primary and secondary axes: `{}`, `{}`",
|
"invalid aligned primary and secondary axes: `{}`, `{}`",
|
||||||
ctx.axes.primary, ctx.axes.secondary));
|
ctx.axes.primary, ctx.axes.secondary,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
ctx.axes = axes;
|
ctx.axes = axes;
|
||||||
}
|
}
|
||||||
@ -106,8 +110,8 @@ function! {
|
|||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
BoxFunc {
|
BoxFunc {
|
||||||
body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
|
body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
|
||||||
extents: AxisMap::parse::<ExtentKey>(&mut f.errors, &mut header.args.key),
|
extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key),
|
||||||
debug: header.args.key.get::<bool>(&mut f.errors, "debug"),
|
debug: header.args.key.get::<bool>(&mut f.problems, "debug"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +123,7 @@ function! {
|
|||||||
ctx.debug = debug;
|
ctx.debug = debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
let map = self.extents.dedup(&mut f.errors, ctx.axes);
|
let map = self.extents.dedup(&mut f.problems, ctx.axes);
|
||||||
for &axis in &[Horizontal, Vertical] {
|
for &axis in &[Horizontal, Vertical] {
|
||||||
if let Some(psize) = map.get(axis) {
|
if let Some(psize) = map.get(axis) {
|
||||||
let size = psize.scaled(ctx.base.get(axis));
|
let size = psize.scaled(ctx.base.get(axis));
|
||||||
|
@ -59,7 +59,7 @@ function! {
|
|||||||
ValFunc { body: body!(opt: body, ctx, f) }
|
ValFunc { body: body!(opt: body, ctx, f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
match &self.body {
|
match &self.body {
|
||||||
Some(model) => vec![LayoutSyntaxModel(model)],
|
Some(model) => vec![LayoutSyntaxModel(model)],
|
||||||
None => vec![],
|
None => vec![],
|
||||||
|
@ -15,9 +15,9 @@ function! {
|
|||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
body!(nope: body, f);
|
body!(nope: body, f);
|
||||||
PageSizeFunc {
|
PageSizeFunc {
|
||||||
paper: header.args.pos.get::<Paper>(&mut f.errors),
|
paper: header.args.pos.get::<Paper>(&mut f.problems),
|
||||||
extents: AxisMap::parse::<ExtentKey>(&mut f.errors, &mut header.args.key),
|
extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key),
|
||||||
flip: header.args.key.get::<bool>(&mut f.errors, "flip").unwrap_or(false),
|
flip: header.args.key.get::<bool>(&mut f.problems, "flip").unwrap_or(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ function! {
|
|||||||
style.class = PaperClass::Custom;
|
style.class = PaperClass::Custom;
|
||||||
}
|
}
|
||||||
|
|
||||||
let map = self.extents.dedup(&mut f.errors, ctx.axes);
|
let map = self.extents.dedup(&mut f.problems, ctx.axes);
|
||||||
map.with(Horizontal, |&width| style.dimensions.x = width);
|
map.with(Horizontal, |&width| style.dimensions.x = width);
|
||||||
map.with(Vertical, |&height| style.dimensions.y = height);
|
map.with(Vertical, |&height| style.dimensions.y = height);
|
||||||
|
|
||||||
@ -53,13 +53,13 @@ function! {
|
|||||||
parse(header, body, ctx, f) {
|
parse(header, body, ctx, f) {
|
||||||
body!(nope: body, f);
|
body!(nope: body, f);
|
||||||
PageMarginsFunc {
|
PageMarginsFunc {
|
||||||
padding: PaddingMap::parse(&mut f.errors, &mut header.args),
|
padding: PaddingMap::parse(&mut f.problems, &mut header.args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, f) {
|
layout(self, ctx, f) {
|
||||||
let mut style = ctx.style.page;
|
let mut style = ctx.style.page;
|
||||||
self.padding.apply(&mut f.errors, ctx.axes, &mut style.margins);
|
self.padding.apply(&mut f.problems, ctx.axes, &mut style.margins);
|
||||||
vec![SetPageStyle(style)]
|
vec![SetPageStyle(style)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ function! {
|
|||||||
pub struct LineBreakFunc;
|
pub struct LineBreakFunc;
|
||||||
|
|
||||||
parse(default)
|
parse(default)
|
||||||
layout(self, ctx, errors) { vec![BreakLine] }
|
layout(self, ctx, f) { vec![BreakLine] }
|
||||||
}
|
}
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
@ -22,7 +22,7 @@ function! {
|
|||||||
pub struct ParBreakFunc;
|
pub struct ParBreakFunc;
|
||||||
|
|
||||||
parse(default)
|
parse(default)
|
||||||
layout(self, ctx, errors) { vec![BreakParagraph] }
|
layout(self, ctx, f) { vec![BreakParagraph] }
|
||||||
}
|
}
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
@ -31,7 +31,7 @@ function! {
|
|||||||
pub struct PageBreakFunc;
|
pub struct PageBreakFunc;
|
||||||
|
|
||||||
parse(default)
|
parse(default)
|
||||||
layout(self, ctx, errors) { vec![BreakPage] }
|
layout(self, ctx, f) { vec![BreakPage] }
|
||||||
}
|
}
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
@ -50,13 +50,13 @@ function! {
|
|||||||
ContentSpacingFunc {
|
ContentSpacingFunc {
|
||||||
body: body!(opt: body, ctx, f),
|
body: body!(opt: body, ctx, f),
|
||||||
content: meta,
|
content: meta,
|
||||||
spacing: header.args.pos.get::<f64>(&mut f.errors)
|
spacing: header.args.pos.get::<f64>(&mut f.problems)
|
||||||
.map(|num| num as f32)
|
.map(|num| num as f32)
|
||||||
.or_missing(&mut f.errors, header.name.span, "spacing"),
|
.or_missing(&mut f.problems, header.name.span, "spacing"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
styled(&self.body, ctx, self.spacing, |t, s| match self.content {
|
styled(&self.body, ctx, self.spacing, |t, s| match self.content {
|
||||||
Word => t.word_spacing_scale = s,
|
Word => t.word_spacing_scale = s,
|
||||||
Line => t.line_spacing_scale = s,
|
Line => t.line_spacing_scale = s,
|
||||||
@ -88,15 +88,15 @@ function! {
|
|||||||
body!(nope: body, f);
|
body!(nope: body, f);
|
||||||
SpacingFunc {
|
SpacingFunc {
|
||||||
spacing: if let Some(axis) = meta {
|
spacing: if let Some(axis) = meta {
|
||||||
header.args.pos.get::<FSize>(&mut f.errors)
|
header.args.pos.get::<FSize>(&mut f.problems)
|
||||||
.map(|s| (AxisKey::Specific(axis), s))
|
.map(|s| (AxisKey::Specific(axis), s))
|
||||||
} else {
|
} else {
|
||||||
header.args.key.get_with_key::<AxisKey, FSize>(&mut f.errors)
|
header.args.key.get_with_key::<AxisKey, FSize>(&mut f.problems)
|
||||||
}.or_missing(&mut f.errors, header.name.span, "spacing"),
|
}.or_missing(&mut f.problems, header.name.span, "spacing"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
if let Some((axis, spacing)) = self.spacing {
|
if let Some((axis, spacing)) = self.spacing {
|
||||||
let axis = axis.to_generic(ctx.axes);
|
let axis = axis.to_generic(ctx.axes);
|
||||||
let spacing = spacing.scaled(ctx.style.text.font_size());
|
let spacing = spacing.scaled(ctx.style.text.font_size());
|
||||||
|
94
src/problem.rs
Normal file
94
src/problem.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
//! Problems (errors / warnings) in _Typst_ documents.
|
||||||
|
//!
|
||||||
|
//! There are no fatal errors in _Typst_. The document will always compile and
|
||||||
|
//! yield a layout. However, this is a best effort process and bad things will
|
||||||
|
//! still generate errors and warnings.
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use crate::syntax::span::SpanVec;
|
||||||
|
|
||||||
|
|
||||||
|
/// A list of spanned problems.
|
||||||
|
pub type Problems = SpanVec<Problem>;
|
||||||
|
|
||||||
|
/// A problem that arose in parsing or layouting.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
|
||||||
|
pub struct Problem {
|
||||||
|
/// How severe / important the problem is.
|
||||||
|
pub severity: Severity,
|
||||||
|
/// A message describing the problem.
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How severe / important a problem is.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum Severity {
|
||||||
|
/// Something in the code is not good.
|
||||||
|
Warning,
|
||||||
|
/// Something in the code is wrong!
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Problem {
|
||||||
|
/// Create a new problem from message and severity.
|
||||||
|
pub fn new(message: impl Into<String>, severity: Severity) -> Self {
|
||||||
|
Self { message: message.into(), severity }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a problem with `Error` severity.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use typstc::error;
|
||||||
|
/// # use typstc::syntax::span::Span;
|
||||||
|
/// # use typstc::Feedback;
|
||||||
|
/// # let span = Span::ZERO;
|
||||||
|
/// # let mut feedback = Feedback::new();
|
||||||
|
/// # let name = "";
|
||||||
|
/// // Create formatted error values.
|
||||||
|
/// let error = error!("expected {}", name);
|
||||||
|
///
|
||||||
|
/// // Create spanned errors.
|
||||||
|
/// let spanned = error!(span, "there is an error here");
|
||||||
|
///
|
||||||
|
/// // Create an error and directly add it to existing feedback.
|
||||||
|
/// error!(@feedback, span, "oh no!");
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! error {
|
||||||
|
($($tts:tt)*) => {
|
||||||
|
$crate::__impl_problem!($crate::problem::Severity::Error; $($tts)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a problem with `Warning` severity.
|
||||||
|
///
|
||||||
|
/// This works exactly like `error!`. See its documentation for more
|
||||||
|
/// information.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! warning {
|
||||||
|
($($tts:tt)*) => {
|
||||||
|
$crate::__impl_problem!($crate::problem::Severity::Warning; $($tts)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Backs the `error!` and `warning!` macros.
|
||||||
|
#[macro_export]
|
||||||
|
#[doc(hidden)]
|
||||||
|
macro_rules! __impl_problem {
|
||||||
|
($severity:expr; @$feedback:expr, $($tts:tt)*) => {
|
||||||
|
$feedback.problems.push($crate::__impl_problem!($severity; $($tts)*));
|
||||||
|
};
|
||||||
|
|
||||||
|
($severity:expr; $fmt:literal $($tts:tt)*) => {
|
||||||
|
$crate::problem::Problem::new(format!($fmt $($tts)*), $severity)
|
||||||
|
};
|
||||||
|
|
||||||
|
($severity:expr; $span:expr, $fmt:literal $($tts:tt)*) => {
|
||||||
|
$crate::syntax::span::Spanned::new(
|
||||||
|
$crate::__impl_problem!($severity; $fmt $($tts)*),
|
||||||
|
$span,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
@ -6,7 +6,7 @@ use std::ops::Deref;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::u8;
|
use std::u8;
|
||||||
|
|
||||||
use crate::error::Errors;
|
use crate::problem::Problems;
|
||||||
use crate::size::Size;
|
use crate::size::Size;
|
||||||
use super::func::{Key, Value};
|
use super::func::{Key, Value};
|
||||||
use super::span::{Span, Spanned};
|
use super::span::{Span, Spanned};
|
||||||
@ -258,28 +258,28 @@ impl Tuple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extract (and remove) the first matching value and remove and generate
|
/// Extract (and remove) the first matching value and remove and generate
|
||||||
/// errors for all previous items that did not match.
|
/// problems for all previous items that did not match.
|
||||||
pub fn get<V: Value>(&mut self, errors: &mut Errors) -> Option<V> {
|
pub fn get<V: Value>(&mut self, problems: &mut Problems) -> Option<V> {
|
||||||
while !self.items.is_empty() {
|
while !self.items.is_empty() {
|
||||||
let expr = self.items.remove(0);
|
let expr = self.items.remove(0);
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
match V::parse(expr) {
|
match V::parse(expr) {
|
||||||
Ok(output) => return Some(output),
|
Ok(output) => return Some(output),
|
||||||
Err(err) => errors.push(Spanned { v: err, span }),
|
Err(v) => problems.push(Spanned { v, span }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract and return an iterator over all values that match and generate
|
/// Extract and return an iterator over all values that match and generate
|
||||||
/// errors for all items that do not match.
|
/// problems for all items that do not match.
|
||||||
pub fn get_all<'a, V: Value>(&'a mut self, errors: &'a mut Errors)
|
pub fn get_all<'a, V: Value>(&'a mut self, problems: &'a mut Problems)
|
||||||
-> impl Iterator<Item=V> + 'a {
|
-> impl Iterator<Item=V> + 'a {
|
||||||
self.items.drain(..).filter_map(move |expr| {
|
self.items.drain(..).filter_map(move |expr| {
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
match V::parse(expr) {
|
match V::parse(expr) {
|
||||||
Ok(output) => Some(output),
|
Ok(output) => Some(output),
|
||||||
Err(err) => { errors.push(Spanned { v: err, span }); None }
|
Err(v) => { problems.push(Spanned { v, span }); None }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -400,9 +400,9 @@ impl Object {
|
|||||||
///
|
///
|
||||||
/// Inserts an error if the value does not match. If the key is not
|
/// Inserts an error if the value does not match. If the key is not
|
||||||
/// contained, no error is inserted.
|
/// contained, no error is inserted.
|
||||||
pub fn get<V: Value>(&mut self, errors: &mut Errors, key: &str) -> Option<V> {
|
pub fn get<V: Value>(&mut self, problems: &mut Problems, key: &str) -> Option<V> {
|
||||||
let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?;
|
let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?;
|
||||||
self.get_index::<V>(errors, index)
|
self.get_index::<V>(problems, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract (and remove) a pair with a matching key and value.
|
/// Extract (and remove) a pair with a matching key and value.
|
||||||
@ -411,12 +411,12 @@ impl Object {
|
|||||||
/// found, no error is inserted.
|
/// found, no error is inserted.
|
||||||
pub fn get_with_key<K: Key, V: Value>(
|
pub fn get_with_key<K: Key, V: Value>(
|
||||||
&mut self,
|
&mut self,
|
||||||
errors: &mut Errors,
|
problems: &mut Problems,
|
||||||
) -> Option<(K, V)> {
|
) -> Option<(K, V)> {
|
||||||
for (index, pair) in self.pairs.iter().enumerate() {
|
for (index, pair) in self.pairs.iter().enumerate() {
|
||||||
let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span };
|
let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span };
|
||||||
if let Some(key) = K::parse(key) {
|
if let Some(key) = K::parse(key) {
|
||||||
return self.get_index::<V>(errors, index).map(|value| (key, value));
|
return self.get_index::<V>(problems, index).map(|value| (key, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -427,7 +427,7 @@ impl Object {
|
|||||||
/// Inserts errors for values that do not match.
|
/// Inserts errors for values that do not match.
|
||||||
pub fn get_all<'a, K: Key, V: Value>(
|
pub fn get_all<'a, K: Key, V: Value>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
errors: &'a mut Errors,
|
problems: &'a mut Problems,
|
||||||
) -> impl Iterator<Item=(K, V)> + 'a {
|
) -> impl Iterator<Item=(K, V)> + 'a {
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
@ -436,7 +436,7 @@ impl Object {
|
|||||||
let key = Spanned { v: key.v.as_str(), span: key.span };
|
let key = Spanned { v: key.v.as_str(), span: key.span };
|
||||||
|
|
||||||
Some(if let Some(key) = K::parse(key) {
|
Some(if let Some(key) = K::parse(key) {
|
||||||
self.get_index::<V>(errors, index).map(|v| (key, v))
|
self.get_index::<V>(problems, index).map(|v| (key, v))
|
||||||
} else {
|
} else {
|
||||||
index += 1;
|
index += 1;
|
||||||
None
|
None
|
||||||
@ -456,20 +456,20 @@ impl Object {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>(
|
pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
errors: &'a mut Errors,
|
problems: &'a mut Problems,
|
||||||
) -> impl Iterator<Item=Spanned<(K, V)>> + 'a {
|
) -> impl Iterator<Item=Spanned<(K, V)>> + 'a {
|
||||||
self.get_all::<Spanned<K>, Spanned<V>>(errors)
|
self.get_all::<Spanned<K>, Spanned<V>>(problems)
|
||||||
.map(|(k, v)| Spanned::new((k.v, v.v), Span::merge(k.span, v.span)))
|
.map(|(k, v)| Spanned::new((k.v, v.v), Span::merge(k.span, v.span)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the argument at the given index and insert an error if the value
|
/// Extract the argument at the given index and insert an error if the value
|
||||||
/// does not match.
|
/// does not match.
|
||||||
fn get_index<V: Value>(&mut self, errors: &mut Errors, index: usize) -> Option<V> {
|
fn get_index<V: Value>(&mut self, problems: &mut Problems, index: usize) -> Option<V> {
|
||||||
let expr = self.pairs.remove(index).v.value;
|
let expr = self.pairs.remove(index).v.value;
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
match V::parse(expr) {
|
match V::parse(expr) {
|
||||||
Ok(output) => Some(output),
|
Ok(output) => Some(output),
|
||||||
Err(err) => { errors.push(Spanned { v: err, span }); None }
|
Err(v) => { problems.push(Spanned { v, span }); None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Deduplicating maps and keys for argument parsing.
|
//! Deduplicating maps and keys for argument parsing.
|
||||||
|
|
||||||
use crate::error::Errors;
|
use crate::problem::Problems;
|
||||||
use crate::layout::prelude::*;
|
use crate::layout::prelude::*;
|
||||||
use crate::size::{PSize, ValueBox};
|
use crate::size::{PSize, ValueBox};
|
||||||
use crate::syntax::span::Spanned;
|
use crate::syntax::span::Spanned;
|
||||||
@ -12,7 +12,7 @@ use super::*;
|
|||||||
/// A map which deduplicates redundant arguments.
|
/// A map which deduplicates redundant arguments.
|
||||||
///
|
///
|
||||||
/// Whenever a duplicate argument is inserted into the map, through the
|
/// Whenever a duplicate argument is inserted into the map, through the
|
||||||
/// functions `from_iter`, `insert` or `extend` an errors is added to the error
|
/// functions `from_iter`, `insert` or `extend` an problems is added to the error
|
||||||
/// list that needs to be passed to those functions.
|
/// list that needs to be passed to those functions.
|
||||||
///
|
///
|
||||||
/// All entries need to have span information to enable the error reporting.
|
/// All entries need to have span information to enable the error reporting.
|
||||||
@ -28,27 +28,27 @@ impl<K, V> DedupMap<K, V> where K: Eq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new map from an iterator of spanned keys and values.
|
/// Create a new map from an iterator of spanned keys and values.
|
||||||
pub fn from_iter<I>(errors: &mut Errors, iter: I) -> DedupMap<K, V>
|
pub fn from_iter<I>(problems: &mut Problems, iter: I) -> DedupMap<K, V>
|
||||||
where I: IntoIterator<Item=Spanned<(K, V)>> {
|
where I: IntoIterator<Item=Spanned<(K, V)>> {
|
||||||
let mut map = DedupMap::new();
|
let mut map = DedupMap::new();
|
||||||
map.extend(errors, iter);
|
map.extend(problems, iter);
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a spanned key-value pair.
|
/// Add a spanned key-value pair.
|
||||||
pub fn insert(&mut self, errors: &mut Errors, entry: Spanned<(K, V)>) {
|
pub fn insert(&mut self, problems: &mut Problems, entry: Spanned<(K, V)>) {
|
||||||
if self.map.iter().any(|e| e.v.0 == entry.v.0) {
|
if self.map.iter().any(|e| e.v.0 == entry.v.0) {
|
||||||
errors.push(err!(entry.span; "duplicate argument"));
|
problems.push(error!(entry.span, "duplicate argument"));
|
||||||
} else {
|
} else {
|
||||||
self.map.push(entry);
|
self.map.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add multiple spanned key-value pairs.
|
/// Add multiple spanned key-value pairs.
|
||||||
pub fn extend<I>(&mut self, errors: &mut Errors, items: I)
|
pub fn extend<I>(&mut self, problems: &mut Problems, items: I)
|
||||||
where I: IntoIterator<Item=Spanned<(K, V)>> {
|
where I: IntoIterator<Item=Spanned<(K, V)>> {
|
||||||
for item in items.into_iter() {
|
for item in items.into_iter() {
|
||||||
self.insert(errors, item);
|
self.insert(problems, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,15 +71,15 @@ impl<K, V> DedupMap<K, V> where K: Eq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new map where keys and values are mapped to new keys and
|
/// Create a new map where keys and values are mapped to new keys and
|
||||||
/// values. When the mapping introduces new duplicates, errors are
|
/// values. When the mapping introduces new duplicates, problems are
|
||||||
/// generated.
|
/// generated.
|
||||||
pub fn dedup<F, K2, V2>(&self, errors: &mut Errors, mut f: F) -> DedupMap<K2, V2>
|
pub fn dedup<F, K2, V2>(&self, problems: &mut Problems, mut f: F) -> DedupMap<K2, V2>
|
||||||
where F: FnMut(&K, &V) -> (K2, V2), K2: Eq {
|
where F: FnMut(&K, &V) -> (K2, V2), K2: Eq {
|
||||||
let mut map = DedupMap::new();
|
let mut map = DedupMap::new();
|
||||||
|
|
||||||
for Spanned { v: (key, value), span } in self.map.iter() {
|
for Spanned { v: (key, value), span } in self.map.iter() {
|
||||||
let (key, value) = f(key, value);
|
let (key, value) = f(key, value);
|
||||||
map.insert(errors, Spanned { v: (key, value), span: *span });
|
map.insert(problems, Spanned { v: (key, value), span: *span });
|
||||||
}
|
}
|
||||||
|
|
||||||
map
|
map
|
||||||
@ -98,21 +98,21 @@ pub struct AxisMap<V>(DedupMap<AxisKey, V>);
|
|||||||
impl<V: Value> AxisMap<V> {
|
impl<V: Value> AxisMap<V> {
|
||||||
/// Parse an axis map from the object.
|
/// Parse an axis map from the object.
|
||||||
pub fn parse<K>(
|
pub fn parse<K>(
|
||||||
errors: &mut Errors,
|
problems: &mut Problems,
|
||||||
object: &mut Object,
|
object: &mut Object,
|
||||||
) -> AxisMap<V> where K: Key + Into<AxisKey> {
|
) -> AxisMap<V> where K: Key + Into<AxisKey> {
|
||||||
let values: Vec<_> = object
|
let values: Vec<_> = object
|
||||||
.get_all_spanned::<K, V>(errors)
|
.get_all_spanned::<K, V>(problems)
|
||||||
.map(|s| s.map(|(k, v)| (k.into(), v)))
|
.map(|s| s.map(|(k, v)| (k.into(), v)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
AxisMap(DedupMap::from_iter(errors, values))
|
AxisMap(DedupMap::from_iter(problems, values))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deduplicate from specific or generic to just specific axes.
|
/// Deduplicate from specific or generic to just specific axes.
|
||||||
pub fn dedup(&self, errors: &mut Errors, axes: LayoutAxes) -> DedupMap<SpecificAxis, V>
|
pub fn dedup(&self, problems: &mut Problems, axes: LayoutAxes) -> DedupMap<SpecificAxis, V>
|
||||||
where V: Clone {
|
where V: Clone {
|
||||||
self.0.dedup(errors, |key, val| (key.to_specific(axes), val.clone()))
|
self.0.dedup(problems, |key, val| (key.to_specific(axes), val.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,23 +124,23 @@ pub struct PosAxisMap<V>(DedupMap<PosAxisKey, V>);
|
|||||||
impl<V: Value> PosAxisMap<V> {
|
impl<V: Value> PosAxisMap<V> {
|
||||||
/// Parse a positional/axis map from the function arguments.
|
/// Parse a positional/axis map from the function arguments.
|
||||||
pub fn parse<K>(
|
pub fn parse<K>(
|
||||||
errors: &mut Errors,
|
problems: &mut Problems,
|
||||||
args: &mut FuncArgs,
|
args: &mut FuncArgs,
|
||||||
) -> PosAxisMap<V> where K: Key + Into<AxisKey> {
|
) -> PosAxisMap<V> where K: Key + Into<AxisKey> {
|
||||||
let mut map = DedupMap::new();
|
let mut map = DedupMap::new();
|
||||||
|
|
||||||
for &key in &[PosAxisKey::First, PosAxisKey::Second] {
|
for &key in &[PosAxisKey::First, PosAxisKey::Second] {
|
||||||
if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(errors) {
|
if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(problems) {
|
||||||
map.insert(errors, Spanned { v: (key, v), span })
|
map.insert(problems, Spanned { v: (key, v), span })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let keywords: Vec<_> = args.key
|
let keywords: Vec<_> = args.key
|
||||||
.get_all_spanned::<K, V>(errors)
|
.get_all_spanned::<K, V>(problems)
|
||||||
.map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v)))
|
.map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
map.extend(errors, keywords);
|
map.extend(problems, keywords);
|
||||||
|
|
||||||
PosAxisMap(map)
|
PosAxisMap(map)
|
||||||
}
|
}
|
||||||
@ -149,7 +149,7 @@ impl<V: Value> PosAxisMap<V> {
|
|||||||
/// or specific axes to just generic axes.
|
/// or specific axes to just generic axes.
|
||||||
pub fn dedup<F>(
|
pub fn dedup<F>(
|
||||||
&self,
|
&self,
|
||||||
errors: &mut Errors,
|
problems: &mut Problems,
|
||||||
axes: LayoutAxes,
|
axes: LayoutAxes,
|
||||||
mut f: F,
|
mut f: F,
|
||||||
) -> DedupMap<GenericAxis, V>
|
) -> DedupMap<GenericAxis, V>
|
||||||
@ -157,7 +157,7 @@ impl<V: Value> PosAxisMap<V> {
|
|||||||
F: FnMut(&V) -> Option<GenericAxis>,
|
F: FnMut(&V) -> Option<GenericAxis>,
|
||||||
V: Clone,
|
V: Clone,
|
||||||
{
|
{
|
||||||
self.0.dedup(errors, |key, val| {
|
self.0.dedup(problems, |key, val| {
|
||||||
(match key {
|
(match key {
|
||||||
PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary),
|
PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary),
|
||||||
PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary),
|
PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary),
|
||||||
@ -175,20 +175,20 @@ pub struct PaddingMap(DedupMap<PaddingKey<AxisKey>, Option<PSize>>);
|
|||||||
|
|
||||||
impl PaddingMap {
|
impl PaddingMap {
|
||||||
/// Parse a padding map from the function arguments.
|
/// Parse a padding map from the function arguments.
|
||||||
pub fn parse(errors: &mut Errors, args: &mut FuncArgs) -> PaddingMap {
|
pub fn parse(problems: &mut Problems, args: &mut FuncArgs) -> PaddingMap {
|
||||||
let mut map = DedupMap::new();
|
let mut map = DedupMap::new();
|
||||||
|
|
||||||
let all = args.pos.get::<Spanned<Defaultable<PSize>>>(errors);
|
let all = args.pos.get::<Spanned<Defaultable<PSize>>>(problems);
|
||||||
if let Some(Spanned { v, span }) = all {
|
if let Some(Spanned { v, span }) = all {
|
||||||
map.insert(errors, Spanned { v: (PaddingKey::All, v.into()), span });
|
map.insert(problems, Spanned { v: (PaddingKey::All, v.into()), span });
|
||||||
}
|
}
|
||||||
|
|
||||||
let paddings: Vec<_> = args.key
|
let paddings: Vec<_> = args.key
|
||||||
.get_all_spanned::<PaddingKey<AxisKey>, Defaultable<PSize>>(errors)
|
.get_all_spanned::<PaddingKey<AxisKey>, Defaultable<PSize>>(problems)
|
||||||
.map(|s| s.map(|(k, v)| (k, v.into())))
|
.map(|s| s.map(|(k, v)| (k, v.into())))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
map.extend(errors, paddings);
|
map.extend(problems, paddings);
|
||||||
|
|
||||||
PaddingMap(map)
|
PaddingMap(map)
|
||||||
}
|
}
|
||||||
@ -196,13 +196,13 @@ impl PaddingMap {
|
|||||||
/// Apply the specified padding on a value box of optional, scalable sizes.
|
/// Apply the specified padding on a value box of optional, scalable sizes.
|
||||||
pub fn apply(
|
pub fn apply(
|
||||||
&self,
|
&self,
|
||||||
errors: &mut Errors,
|
problems: &mut Problems,
|
||||||
axes: LayoutAxes,
|
axes: LayoutAxes,
|
||||||
padding: &mut ValueBox<Option<PSize>>
|
padding: &mut ValueBox<Option<PSize>>
|
||||||
) {
|
) {
|
||||||
use PaddingKey::*;
|
use PaddingKey::*;
|
||||||
|
|
||||||
let map = self.0.dedup(errors, |key, &val| {
|
let map = self.0.dedup(problems, |key, &val| {
|
||||||
(match key {
|
(match key {
|
||||||
All => All,
|
All => All,
|
||||||
Both(axis) => Both(axis.to_specific(axes)),
|
Both(axis) => Both(axis.to_specific(axes)),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Primitives for argument parsing in library functions.
|
//! Primitives for argument parsing in library functions.
|
||||||
|
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use crate::error::{Error, Errors};
|
use crate::problem::{Problem, Problems};
|
||||||
use super::expr::{Expr, Ident, Tuple, Object, Pair};
|
use super::expr::{Expr, Ident, Tuple, Object, Pair};
|
||||||
use super::span::{Span, Spanned};
|
use super::span::{Span, Spanned};
|
||||||
|
|
||||||
@ -84,13 +84,13 @@ pub enum FuncArg {
|
|||||||
pub trait OptionExt: Sized {
|
pub trait OptionExt: Sized {
|
||||||
/// Add an error about a missing argument `arg` with the given span if the
|
/// Add an error about a missing argument `arg` with the given span if the
|
||||||
/// option is `None`.
|
/// option is `None`.
|
||||||
fn or_missing(self, errors: &mut Errors, span: Span, arg: &str) -> Self;
|
fn or_missing(self, problems: &mut Problems, span: Span, arg: &str) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OptionExt for Option<T> {
|
impl<T> OptionExt for Option<T> {
|
||||||
fn or_missing(self, errors: &mut Errors, span: Span, arg: &str) -> Self {
|
fn or_missing(self, problems: &mut Problems, span: Span, arg: &str) -> Self {
|
||||||
if self.is_none() {
|
if self.is_none() {
|
||||||
errors.push(err!(span; "missing argument: {}", arg));
|
problems.push(error!(span, "missing argument: {}", arg));
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -23,20 +23,20 @@ use self::AlignmentValue::*;
|
|||||||
/// # Example implementation
|
/// # Example implementation
|
||||||
/// An implementation for `bool` might look as follows:
|
/// An implementation for `bool` might look as follows:
|
||||||
/// ```
|
/// ```
|
||||||
/// # use typstc::err;
|
/// # use typstc::error;
|
||||||
/// # use typstc::error::Error;
|
/// # use typstc::problem::Problem;
|
||||||
/// # use typstc::syntax::expr::Expr;
|
/// # use typstc::syntax::expr::Expr;
|
||||||
/// # use typstc::syntax::func::Value;
|
/// # use typstc::syntax::func::Value;
|
||||||
/// # use typstc::syntax::span::Spanned;
|
/// # use typstc::syntax::span::Spanned;
|
||||||
/// # struct Bool; /*
|
/// # struct Bool; /*
|
||||||
/// impl Value for bool {
|
/// impl Value for bool {
|
||||||
/// # */ impl Value for Bool {
|
/// # */ impl Value for Bool {
|
||||||
/// fn parse(expr: Spanned<Expr>) -> Result<Self, Error> {
|
/// fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||||
/// match expr.v {
|
/// match expr.v {
|
||||||
/// # /*
|
/// # /*
|
||||||
/// Expr::Bool(b) => Ok(b),
|
/// Expr::Bool(b) => Ok(b),
|
||||||
/// # */ Expr::Bool(_) => Ok(Bool),
|
/// # */ Expr::Bool(_) => Ok(Bool),
|
||||||
/// other => Err(err!("expected bool, found {}", other.name())),
|
/// other => Err(error!("expected bool, found {}", other.name())),
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
@ -44,11 +44,11 @@ use self::AlignmentValue::*;
|
|||||||
pub trait Value: Sized {
|
pub trait Value: Sized {
|
||||||
/// Parse an expression into this value or return an error if the expression
|
/// Parse an expression into this value or return an error if the expression
|
||||||
/// is valid for this value type.
|
/// is valid for this value type.
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Error>;
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Value> Value for Spanned<V> {
|
impl<V: Value> Value for Spanned<V> {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
V::parse(expr).map(|v| Spanned { v, span })
|
V::parse(expr).map(|v| Spanned { v, span })
|
||||||
}
|
}
|
||||||
@ -58,12 +58,13 @@ impl<V: Value> Value for Spanned<V> {
|
|||||||
macro_rules! value {
|
macro_rules! value {
|
||||||
($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => {
|
($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => {
|
||||||
impl Value for $type {
|
impl Value for $type {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
match expr.v {
|
match expr.v {
|
||||||
$($p => Ok($r)),*,
|
$($p => Ok($r)),*,
|
||||||
other => Err(err!("expected {}, found {}",
|
other => Err(
|
||||||
$name, other.name())),
|
error!("expected {}, found {}", $name, other.name())
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,7 +121,7 @@ impl From<StringLike> for String {
|
|||||||
pub struct Defaultable<V>(pub Option<V>);
|
pub struct Defaultable<V>(pub Option<V>);
|
||||||
|
|
||||||
impl<V: Value> Value for Defaultable<V> {
|
impl<V: Value> Value for Defaultable<V> {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||||
Ok(Defaultable(match expr.v {
|
Ok(Defaultable(match expr.v {
|
||||||
Expr::Ident(ident) if ident.as_str() == "default" => None,
|
Expr::Ident(ident) if ident.as_str() == "default" => None,
|
||||||
_ => Some(V::parse(expr)?)
|
_ => Some(V::parse(expr)?)
|
||||||
@ -135,16 +136,16 @@ impl<V> From<Defaultable<V>> for Option<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Value for FontStyle {
|
impl Value for FontStyle {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||||
FontStyle::from_name(Ident::parse(expr)?.as_str())
|
FontStyle::from_name(Ident::parse(expr)?.as_str())
|
||||||
.ok_or_else(|| err!("invalid font style"))
|
.ok_or_else(|| error!("invalid font style"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The additional boolean specifies whether a number was clamped into the range
|
/// The additional boolean specifies whether a number was clamped into the range
|
||||||
/// 100 - 900 to make it a valid font weight.
|
/// 100 - 900 to make it a valid font weight.
|
||||||
impl Value for (FontWeight, bool) {
|
impl Value for (FontWeight, bool) {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||||
match expr.v {
|
match expr.v {
|
||||||
Expr::Number(weight) => {
|
Expr::Number(weight) => {
|
||||||
let weight = weight.round();
|
let weight = weight.round();
|
||||||
@ -158,30 +159,31 @@ impl Value for (FontWeight, bool) {
|
|||||||
}
|
}
|
||||||
Expr::Ident(id) => {
|
Expr::Ident(id) => {
|
||||||
FontWeight::from_name(id.as_str())
|
FontWeight::from_name(id.as_str())
|
||||||
.ok_or_else(|| err!("invalid font weight"))
|
.ok_or_else(|| error!("invalid font weight"))
|
||||||
.map(|weight| (weight, false))
|
.map(|weight| (weight, false))
|
||||||
}
|
}
|
||||||
other => Err(err!("expected identifier or number, \
|
other => Err(
|
||||||
found {}", other.name())),
|
error!("expected identifier or number, found {}", other.name())
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value for Paper {
|
impl Value for Paper {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||||
Paper::from_name(Ident::parse(expr)?.as_str())
|
Paper::from_name(Ident::parse(expr)?.as_str())
|
||||||
.ok_or_else(|| err!("invalid paper type"))
|
.ok_or_else(|| error!("invalid paper type"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value for Direction {
|
impl Value for Direction {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||||
Ok(match Ident::parse(expr)?.as_str() {
|
Ok(match Ident::parse(expr)?.as_str() {
|
||||||
"left-to-right" | "ltr" | "LTR" => LeftToRight,
|
"left-to-right" | "ltr" | "LTR" => LeftToRight,
|
||||||
"right-to-left" | "rtl" | "RTL" => RightToLeft,
|
"right-to-left" | "rtl" | "RTL" => RightToLeft,
|
||||||
"top-to-bottom" | "ttb" | "TTB" => TopToBottom,
|
"top-to-bottom" | "ttb" | "TTB" => TopToBottom,
|
||||||
"bottom-to-top" | "btt" | "BTT" => BottomToTop,
|
"bottom-to-top" | "btt" | "BTT" => BottomToTop,
|
||||||
_ => return Err(err!("invalid direction"))
|
_ => return Err(error!("invalid direction"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,7 +250,7 @@ impl AlignmentValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Value for AlignmentValue {
|
impl Value for AlignmentValue {
|
||||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> {
|
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
|
||||||
Ok(match Ident::parse(expr)?.as_str() {
|
Ok(match Ident::parse(expr)?.as_str() {
|
||||||
"origin" => Align(Origin),
|
"origin" => Align(Origin),
|
||||||
"center" => Align(Center),
|
"center" => Align(Center),
|
||||||
@ -257,7 +259,7 @@ impl Value for AlignmentValue {
|
|||||||
"top" => Top,
|
"top" => Top,
|
||||||
"right" => Right,
|
"right" => Right,
|
||||||
"bottom" => Bottom,
|
"bottom" => Bottom,
|
||||||
_ => return Err(err!("invalid alignment"))
|
_ => return Err(error!("invalid alignment"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass<SyntaxModel>
|
|||||||
feedback.extend_offset(span.start, parsed.feedback);
|
feedback.extend_offset(span.start, parsed.feedback);
|
||||||
|
|
||||||
if !terminated {
|
if !terminated {
|
||||||
feedback.errors.push(err!(Span::at(span.end);
|
error!(@feedback, Span::at(span.end), "expected closing bracket");
|
||||||
"expected closing bracket"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed.output
|
parsed.output
|
||||||
@ -62,8 +61,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass<SyntaxModel>
|
|||||||
|
|
||||||
Token::Raw { raw, terminated } => {
|
Token::Raw { raw, terminated } => {
|
||||||
if !terminated {
|
if !terminated {
|
||||||
feedback.errors.push(err!(Span::at(span.end);
|
error!(@feedback, Span::at(span.end), "expected backtick");
|
||||||
"expected backtick"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Node::Raw(unescape_raw(raw))
|
Node::Raw(unescape_raw(raw))
|
||||||
@ -72,7 +70,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass<SyntaxModel>
|
|||||||
Token::Text(text) => Node::Text(text.to_string()),
|
Token::Text(text) => Node::Text(text.to_string()),
|
||||||
|
|
||||||
other => {
|
other => {
|
||||||
feedback.errors.push(err!(span; "unexpected {}", other.name()));
|
error!(@feedback, span, "unexpected {}", other.name());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -129,7 +127,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
|
|
||||||
// The fallback parser was returned. Invalid function.
|
// The fallback parser was returned. Invalid function.
|
||||||
Err(parser) => {
|
Err(parser) => {
|
||||||
self.feedback.errors.push(err!(header.name.span; "unknown function"));
|
error!(@self.feedback, header.name.span, "unknown function");
|
||||||
(parser, Decoration::InvalidFuncName)
|
(parser, Decoration::InvalidFuncName)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -270,10 +268,10 @@ impl<'s> FuncParser<'s> {
|
|||||||
let expr = binop(Box::new(o1), Box::new(o2));
|
let expr = binop(Box::new(o1), Box::new(o2));
|
||||||
return Some(Spanned::new(expr, span));
|
return Some(Spanned::new(expr, span));
|
||||||
} else {
|
} else {
|
||||||
self.feedback.errors.push(err!(
|
error!(
|
||||||
Span::merge(next.span, o1.span);
|
@self.feedback, Span::merge(next.span, o1.span),
|
||||||
"missing right {}", operand_name,
|
"missing right {}", operand_name,
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,7 +290,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
let span = Span::merge(first.span, factor.span);
|
let span = Span::merge(first.span, factor.span);
|
||||||
Some(Spanned::new(Expr::Neg(Box::new(factor)), span))
|
Some(Spanned::new(Expr::Neg(Box::new(factor)), span))
|
||||||
} else {
|
} else {
|
||||||
self.feedback.errors.push(err!(first.span; "dangling minus"));
|
error!(@self.feedback, first.span, "dangling minus");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -333,7 +331,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
take!(Expr::Color(color))
|
take!(Expr::Color(color))
|
||||||
} else {
|
} else {
|
||||||
// Heal color by assuming black
|
// Heal color by assuming black
|
||||||
self.feedback.errors.push(err!(first.span; "invalid color"));
|
error!(@self.feedback, first.span, "invalid color");
|
||||||
take!(Expr::Color(RgbaColor::new_healed(0, 0, 0, 255)))
|
take!(Expr::Color(RgbaColor::new_healed(0, 0, 0, 255)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -517,14 +515,16 @@ impl<'s> FuncParser<'s> {
|
|||||||
/// Add an error about an expected `thing` which was not found, showing
|
/// Add an error about an expected `thing` which was not found, showing
|
||||||
/// what was found instead.
|
/// what was found instead.
|
||||||
fn expected_found(&mut self, thing: &str, found: Spanned<Token>) {
|
fn expected_found(&mut self, thing: &str, found: Spanned<Token>) {
|
||||||
self.feedback.errors.push(err!(found.span;
|
error!(
|
||||||
"expected {}, found {}", thing, found.v.name()));
|
@self.feedback, found.span,
|
||||||
|
"expected {}, found {}", thing, found.v.name(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an error about an `thing` which was expected but not found at the
|
/// Add an error about an `thing` which was expected but not found at the
|
||||||
/// given position.
|
/// given position.
|
||||||
fn expected_at(&mut self, thing: &str, pos: Position) {
|
fn expected_at(&mut self, thing: &str, pos: Position) {
|
||||||
self.feedback.errors.push(err!(Span::at(pos); "expected {}", thing));
|
error!(@self.feedback, Span::at(pos), "expected {}", thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a expected-found-error if `found` is `Some` and an expected-error
|
/// Add a expected-found-error if `found` is `Some` and an expected-error
|
||||||
@ -726,7 +726,7 @@ mod tests {
|
|||||||
p!($source => [$($model)*], []);
|
p!($source => [$($model)*], []);
|
||||||
};
|
};
|
||||||
|
|
||||||
($source:expr => [$($model:tt)*], [$($errors:tt)*] $(, [$($decos:tt)*])? $(,)?) => {
|
($source:expr => [$($model:tt)*], [$($problems:tt)*] $(, [$($decos:tt)*])? $(,)?) => {
|
||||||
let mut scope = Scope::new::<DebugFn>();
|
let mut scope = Scope::new::<DebugFn>();
|
||||||
scope.add::<DebugFn>("f");
|
scope.add::<DebugFn>("f");
|
||||||
scope.add::<DebugFn>("n");
|
scope.add::<DebugFn>("n");
|
||||||
@ -740,12 +740,12 @@ mod tests {
|
|||||||
let (exp, cmp) = spanned![vec $($model)*];
|
let (exp, cmp) = spanned![vec $($model)*];
|
||||||
check($source, exp, pass.output.nodes, cmp);
|
check($source, exp, pass.output.nodes, cmp);
|
||||||
|
|
||||||
// Test errors
|
// Test problems
|
||||||
let (exp, cmp) = spanned![vec $($errors)*];
|
let (exp, cmp) = spanned![vec $($problems)*];
|
||||||
let exp = exp.into_iter()
|
let exp = exp.into_iter()
|
||||||
.map(|s: Spanned<&str>| s.map(|e| e.to_string()))
|
.map(|s: Spanned<&str>| s.map(|e| e.to_string()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let found = pass.feedback.errors.into_iter()
|
let found = pass.feedback.problems.into_iter()
|
||||||
.map(|s| s.map(|e| e.message))
|
.map(|s| s.map(|e| e.message))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
check($source, exp, found, cmp);
|
check($source, exp, found, cmp);
|
||||||
|
@ -76,7 +76,7 @@ pub struct Span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
/// A dummy span.
|
/// The zero span.
|
||||||
pub const ZERO: Span = Span { start: Position::ZERO, end: Position::ZERO };
|
pub const ZERO: Span = Span { start: Position::ZERO, end: Position::ZERO };
|
||||||
|
|
||||||
/// Create a new span from start and end positions.
|
/// Create a new span from start and end positions.
|
||||||
|
@ -29,7 +29,7 @@ where T: Debug + PartialEq + SpanlessEq {
|
|||||||
/// spanned![(0:0, 0:5, "hello"), (0:5, 0:3, "world")]
|
/// spanned![(0:0, 0:5, "hello"), (0:5, 0:3, "world")]
|
||||||
/// ```
|
/// ```
|
||||||
/// The span information can simply be omitted to create a vector with items
|
/// The span information can simply be omitted to create a vector with items
|
||||||
/// that are spanned with dummy zero spans.
|
/// that are spanned with zero spans.
|
||||||
macro_rules! spanned {
|
macro_rules! spanned {
|
||||||
(item ($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => ({
|
(item ($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => ({
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@ -80,7 +80,7 @@ function! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) { vec![] }
|
layout(self, ctx, f) { vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compares elements by only looking at values and ignoring spans.
|
/// Compares elements by only looking at values and ignoring spans.
|
||||||
|
@ -123,14 +123,14 @@ fn test(name: &str, src: &str) -> DynResult<()> {
|
|||||||
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
|
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
let typeset = block_on(typesetter.typeset(src));
|
let typeset = block_on(typesetter.typeset(src));
|
||||||
let errors = typeset.feedback.errors;
|
let problems = typeset.feedback.problems;
|
||||||
|
|
||||||
if !errors.is_empty() {
|
if !problems.is_empty() {
|
||||||
for error in errors {
|
for problem in problems {
|
||||||
println!(" {:?} {:?}: {}",
|
println!(" {:?} {:?}: {}",
|
||||||
error.v.severity,
|
problem.v.severity,
|
||||||
error.span,
|
problem.span,
|
||||||
error.v.message
|
problem.v.message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user