A few small improvements ♻

This commit is contained in:
Laurenz 2021-01-01 17:54:31 +01:00
parent 2b6ccd8248
commit 8cad78481c
15 changed files with 201 additions and 228 deletions

View File

@ -104,16 +104,16 @@ impl Display for Level {
#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum Deco { pub enum Deco {
/// Emphasized text.
Emph,
/// Strong text. /// Strong text.
Strong, Strong,
/// Emphasized text.
Emph,
/// A valid, successfully resolved name. /// A valid, successfully resolved name.
Resolved, Resolved,
/// An invalid, unresolved name. /// An invalid, unresolved name.
Unresolved, Unresolved,
/// A key in a dictionary. /// A name in a dictionary or argument list.
DictKey, Name,
} }
/// Construct a diagnostic with [`Error`](Level::Error) level. /// Construct a diagnostic with [`Error`](Level::Error) level.

View File

@ -4,7 +4,7 @@ use std::mem;
use super::{Conv, EvalContext, RefKey, TryFromValue, Value, ValueDict}; use super::{Conv, EvalContext, RefKey, TryFromValue, Value, ValueDict};
use crate::diag::Diag; use crate::diag::Diag;
use crate::syntax::{Span, SpanVec, SpanWith, Spanned}; use crate::syntax::{Span, SpanVec, Spanned, WithSpan};
/// A wrapper around a dictionary value that simplifies argument parsing in /// A wrapper around a dictionary value that simplifies argument parsing in
/// functions. /// functions.
@ -23,7 +23,11 @@ impl Args {
{ {
self.0.v.remove(key).and_then(|entry| { self.0.v.remove(key).and_then(|entry| {
let span = entry.value.span; let span = entry.value.span;
conv_diag(T::try_from_value(entry.value), &mut ctx.f.diags, span) conv_diag(
T::try_from_value(entry.value),
&mut ctx.feedback.diags,
span,
)
}) })
} }
@ -41,9 +45,13 @@ impl Args {
{ {
if let Some(entry) = self.0.v.remove(key) { if let Some(entry) = self.0.v.remove(key) {
let span = entry.value.span; let span = entry.value.span;
conv_diag(T::try_from_value(entry.value), &mut ctx.f.diags, span) conv_diag(
T::try_from_value(entry.value),
&mut ctx.feedback.diags,
span,
)
} else { } else {
ctx.f.diags.push(error!(self.0.span, "missing argument: {}", name)); ctx.diag(error!(self.0.span, "missing argument: {}", name));
None None
} }
} }
@ -122,11 +130,11 @@ fn conv_diag<T>(conv: Conv<T>, diags: &mut SpanVec<Diag>, span: Span) -> Option<
match conv { match conv {
Conv::Ok(t) => Some(t), Conv::Ok(t) => Some(t),
Conv::Warn(t, warn) => { Conv::Warn(t, warn) => {
diags.push(warn.span_with(span)); diags.push(warn.with_span(span));
Some(t) Some(t)
} }
Conv::Err(_, err) => { Conv::Err(_, err) => {
diags.push(err.span_with(span)); diags.push(err.with_span(span));
None None
} }
} }
@ -137,7 +145,7 @@ fn conv_put_back<T>(conv: Conv<T>, slot: &mut Spanned<Value>, span: Span) -> Opt
Conv::Ok(t) => Some(t), Conv::Ok(t) => Some(t),
Conv::Warn(t, _) => Some(t), Conv::Warn(t, _) => Some(t),
Conv::Err(v, _) => { Conv::Err(v, _) => {
*slot = v.span_with(span); *slot = v.with_span(span);
None None
} }
} }

View File

@ -48,7 +48,7 @@ pub struct EvalContext {
/// The active evaluation state. /// The active evaluation state.
pub state: State, pub state: State,
/// The accumulated feedback. /// The accumulated feedback.
f: Feedback, feedback: Feedback,
/// The finished page runs. /// The finished page runs.
runs: Vec<Pages>, runs: Vec<Pages>,
/// The stack of logical groups (paragraphs and such). /// The stack of logical groups (paragraphs and such).
@ -71,24 +71,24 @@ impl EvalContext {
groups: vec![], groups: vec![],
inner: vec![], inner: vec![],
runs: vec![], runs: vec![],
f: Feedback::new(), feedback: Feedback::new(),
} }
} }
/// Finish evaluation and return the created document. /// Finish evaluation and return the created document.
pub fn finish(self) -> Pass<Document> { pub fn finish(self) -> Pass<Document> {
assert!(self.groups.is_empty(), "unfinished group"); assert!(self.groups.is_empty(), "unfinished group");
Pass::new(Document { runs: self.runs }, self.f) Pass::new(Document { runs: self.runs }, self.feedback)
} }
/// Add a diagnostic to the feedback. /// Add a diagnostic to the feedback.
pub fn diag(&mut self, diag: Spanned<Diag>) { pub fn diag(&mut self, diag: Spanned<Diag>) {
self.f.diags.push(diag); self.feedback.diags.push(diag);
} }
/// Add a decoration to the feedback. /// Add a decoration to the feedback.
pub fn deco(&mut self, deco: Spanned<Deco>) { pub fn deco(&mut self, deco: Spanned<Deco>) {
self.f.decos.push(deco); self.feedback.decos.push(deco);
} }
/// Push a layout node to the active group. /// Push a layout node to the active group.
@ -246,6 +246,23 @@ impl EvalContext {
} }
} }
/// Apply a forced line break.
pub fn apply_linebreak(&mut self) {
self.end_par_group();
self.start_par_group();
}
/// Apply a forced paragraph break.
pub fn apply_parbreak(&mut self) {
self.end_par_group();
let em = self.state.font.font_size();
self.push(Spacing {
amount: self.state.par.par_spacing.resolve(em),
softness: Softness::Soft,
});
self.start_par_group();
}
/// Construct a text node from the given string based on the active text /// Construct a text node from the given string based on the active text
/// state. /// state.
pub fn make_text_node(&self, text: String) -> Text { pub fn make_text_node(&self, text: String) -> Text {
@ -293,12 +310,12 @@ struct ParGroup {
line_spacing: Length, line_spacing: Length,
} }
/// Defines how items interact with surrounding items. /// Defines how an item interact with surrounding items.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Softness { pub enum Softness {
/// Soft items can be skipped in some circumstances. /// A soft item can be skipped in some circumstances.
Soft, Soft,
/// Hard items are always retained. /// A hard item is always retained.
Hard, Hard,
} }
@ -310,24 +327,35 @@ pub trait Eval {
type Output; type Output;
/// Evaluate the item to the output value. /// Evaluate the item to the output value.
fn eval(&self, ctx: &mut EvalContext) -> Self::Output; fn eval(self, ctx: &mut EvalContext) -> Self::Output;
} }
impl Eval for SynTree { impl<'a, T> Eval for &'a Box<Spanned<T>>
where
Spanned<&'a T>: Eval,
{
type Output = <Spanned<&'a T> as Eval>::Output;
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
(**self).as_ref().eval(ctx)
}
}
impl Eval for &[Spanned<SynNode>] {
type Output = (); type Output = ();
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
for node in self { for node in self {
node.v.eval(ctx); node.as_ref().eval(ctx);
} }
} }
} }
impl Eval for SynNode { impl Eval for Spanned<&SynNode> {
type Output = (); type Output = ();
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
match self { match self.v {
SynNode::Text(text) => { SynNode::Text(text) => {
let node = ctx.make_text_node(text.clone()); let node = ctx.make_text_node(text.clone());
ctx.push(node); ctx.push(node);
@ -340,53 +368,43 @@ impl Eval for SynNode {
softness: Softness::Soft, softness: Softness::Soft,
}); });
} }
SynNode::Linebreak => ctx.apply_linebreak(),
SynNode::Linebreak => { SynNode::Parbreak => ctx.apply_parbreak(),
ctx.end_par_group();
ctx.start_par_group();
}
SynNode::Parbreak => {
ctx.end_par_group();
let em = ctx.state.font.font_size();
ctx.push(Spacing {
amount: ctx.state.par.par_spacing.resolve(em),
softness: Softness::Soft,
});
ctx.start_par_group();
}
SynNode::Strong => ctx.state.font.strong ^= true, SynNode::Strong => ctx.state.font.strong ^= true,
SynNode::Emph => ctx.state.font.emph ^= true, SynNode::Emph => ctx.state.font.emph ^= true,
SynNode::Heading(heading) => heading.eval(ctx), SynNode::Heading(heading) => heading.with_span(self.span).eval(ctx),
SynNode::Raw(raw) => raw.eval(ctx), SynNode::Raw(raw) => raw.with_span(self.span).eval(ctx),
SynNode::Expr(expr) => expr.eval(ctx).eval(ctx), SynNode::Expr(expr) => {
let value = expr.with_span(self.span).eval(ctx);
value.eval(ctx)
}
} }
} }
} }
impl Eval for NodeHeading { impl Eval for Spanned<&NodeHeading> {
type Output = (); type Output = ();
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
let prev = ctx.state.clone(); let prev = ctx.state.clone();
let upscale = 1.5 - 0.1 * self.level.v as f64; let upscale = 1.5 - 0.1 * self.v.level.v as f64;
ctx.state.font.scale *= upscale; ctx.state.font.scale *= upscale;
ctx.state.font.strong = true; ctx.state.font.strong = true;
self.contents.eval(ctx); self.v.contents.eval(ctx);
SynNode::Parbreak.eval(ctx); ctx.apply_parbreak();
ctx.state = prev; ctx.state = prev;
} }
} }
impl Eval for NodeRaw { impl Eval for Spanned<&NodeRaw> {
type Output = (); type Output = ();
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
let prev = Rc::clone(&ctx.state.font.families); let prev = Rc::clone(&ctx.state.font.families);
let families = Rc::make_mut(&mut ctx.state.font.families); let families = Rc::make_mut(&mut ctx.state.font.families);
families.list.insert(0, "monospace".to_string()); families.list.insert(0, "monospace".to_string());
@ -396,7 +414,7 @@ impl Eval for NodeRaw {
let line_spacing = ctx.state.par.line_spacing.resolve(em); let line_spacing = ctx.state.par.line_spacing.resolve(em);
let mut children = vec![]; let mut children = vec![];
for line in &self.lines { for line in &self.v.lines {
children.push(LayoutNode::Text(ctx.make_text_node(line.clone()))); children.push(LayoutNode::Text(ctx.make_text_node(line.clone())));
children.push(LayoutNode::Spacing(Spacing { children.push(LayoutNode::Spacing(Spacing {
amount: line_spacing, amount: line_spacing,
@ -415,24 +433,24 @@ impl Eval for NodeRaw {
} }
} }
impl Eval for Expr { impl Eval for Spanned<&Expr> {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
match self { match self.v {
Self::Lit(lit) => lit.eval(ctx), Expr::Lit(lit) => lit.with_span(self.span).eval(ctx),
Self::Call(call) => call.eval(ctx), Expr::Call(call) => call.with_span(self.span).eval(ctx),
Self::Unary(unary) => unary.eval(ctx), Expr::Unary(unary) => unary.with_span(self.span).eval(ctx),
Self::Binary(binary) => binary.eval(ctx), Expr::Binary(binary) => binary.with_span(self.span).eval(ctx),
} }
} }
} }
impl Eval for Lit { impl Eval for Spanned<&Lit> {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
match *self { match *self.v {
Lit::Ident(ref v) => Value::Ident(v.clone()), Lit::Ident(ref v) => Value::Ident(v.clone()),
Lit::Bool(v) => Value::Bool(v), Lit::Bool(v) => Value::Bool(v),
Lit::Int(v) => Value::Int(v), Lit::Int(v) => Value::Int(v),
@ -441,20 +459,20 @@ impl Eval for Lit {
Lit::Percent(v) => Value::Relative(Relative::new(v / 100.0)), Lit::Percent(v) => Value::Relative(Relative::new(v / 100.0)),
Lit::Color(v) => Value::Color(Color::Rgba(v)), Lit::Color(v) => Value::Color(Color::Rgba(v)),
Lit::Str(ref v) => Value::Str(v.clone()), Lit::Str(ref v) => Value::Str(v.clone()),
Lit::Dict(ref v) => Value::Dict(v.eval(ctx)), Lit::Dict(ref v) => Value::Dict(v.with_span(self.span).eval(ctx)),
Lit::Content(ref v) => Value::Content(v.clone()), Lit::Content(ref v) => Value::Content(v.clone()),
} }
} }
} }
impl Eval for LitDict { impl Eval for Spanned<&LitDict> {
type Output = ValueDict; type Output = ValueDict;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
let mut dict = ValueDict::new(); let mut dict = ValueDict::new();
for entry in &self.0 { for entry in &self.v.0 {
let val = entry.expr.v.eval(ctx); let val = entry.expr.as_ref().eval(ctx);
let spanned = val.span_with(entry.expr.span); let spanned = val.with_span(entry.expr.span);
if let Some(key) = &entry.key { if let Some(key) = &entry.key {
dict.insert(&key.v, SpannedEntry::new(key.span, spanned)); dict.insert(&key.v, SpannedEntry::new(key.span, spanned));
} else { } else {
@ -466,58 +484,58 @@ impl Eval for LitDict {
} }
} }
impl Eval for ExprCall { impl Eval for Spanned<&ExprCall> {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
let name = &self.name.v; let name = &self.v.name.v;
let span = self.name.span; let span = self.v.name.span;
let dict = self.args.v.eval(ctx); let dict = self.v.args.as_ref().eval(ctx);
if let Some(func) = ctx.state.scope.get(name) { if let Some(func) = ctx.state.scope.get(name) {
let args = Args(dict.span_with(self.args.span)); let args = Args(dict.with_span(self.v.args.span));
ctx.f.decos.push(Deco::Resolved.span_with(span)); ctx.feedback.decos.push(Deco::Resolved.with_span(span));
(func.clone())(args, ctx) (func.clone())(args, ctx)
} else { } else {
if !name.is_empty() { if !name.is_empty() {
ctx.diag(error!(span, "unknown function")); ctx.diag(error!(span, "unknown function"));
ctx.f.decos.push(Deco::Unresolved.span_with(span)); ctx.feedback.decos.push(Deco::Unresolved.with_span(span));
} }
Value::Dict(dict) Value::Dict(dict)
} }
} }
} }
impl Eval for ExprUnary { impl Eval for Spanned<&ExprUnary> {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
let value = self.expr.v.eval(ctx); let value = self.v.expr.eval(ctx);
if let Value::Error = value { if let Value::Error = value {
return Value::Error; return Value::Error;
} }
let span = self.op.span.join(self.expr.span); let span = self.v.op.span.join(self.v.expr.span);
match self.op.v { match self.v.op.v {
UnOp::Neg => neg(ctx, span, value), UnOp::Neg => neg(ctx, span, value),
} }
} }
} }
impl Eval for ExprBinary { impl Eval for Spanned<&ExprBinary> {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
let lhs = self.lhs.v.eval(ctx); let lhs = self.v.lhs.eval(ctx);
let rhs = self.rhs.v.eval(ctx); let rhs = self.v.rhs.eval(ctx);
if lhs == Value::Error || rhs == Value::Error { if lhs == Value::Error || rhs == Value::Error {
return Value::Error; return Value::Error;
} }
let span = self.lhs.span.join(self.rhs.span); let span = self.v.lhs.span.join(self.v.rhs.span);
match self.op.v { match self.v.op.v {
BinOp::Add => add(ctx, span, lhs, rhs), BinOp::Add => add(ctx, span, lhs, rhs),
BinOp::Sub => sub(ctx, span, lhs, rhs), BinOp::Sub => sub(ctx, span, lhs, rhs),
BinOp::Mul => mul(ctx, span, lhs, rhs), BinOp::Mul => mul(ctx, span, lhs, rhs),

View File

@ -11,7 +11,7 @@ use crate::color::Color;
use crate::diag::Diag; use crate::diag::Diag;
use crate::geom::{Dir, Length, Linear, Relative}; use crate::geom::{Dir, Length, Linear, Relative};
use crate::paper::Paper; use crate::paper::Paper;
use crate::syntax::{Ident, SpanWith, Spanned, SynTree}; use crate::syntax::{Ident, Spanned, SynTree, WithSpan};
/// A computational value. /// A computational value.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
@ -69,11 +69,11 @@ impl Value {
} }
} }
impl Eval for Value { impl Eval for &Value {
type Output = (); type Output = ();
/// Evaluate everything contained in this value. /// Evaluate everything contained in this value.
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
match self { match self {
// Don't print out none values. // Don't print out none values.
Value::None => {} Value::None => {}
@ -206,7 +206,7 @@ impl<T> Conv<T> {
impl<T: TryFromValue> TryFromValue for Spanned<T> { impl<T: TryFromValue> TryFromValue for Spanned<T> {
fn try_from_value(value: Spanned<Value>) -> Conv<Self> { fn try_from_value(value: Spanned<Value>) -> Conv<Self> {
let span = value.span; let span = value.span;
T::try_from_value(value).map(|v| v.span_with(span)) T::try_from_value(value).map(|v| v.with_span(span))
} }
} }

View File

@ -2,7 +2,6 @@
use std::any::Any; use std::any::Any;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::ops::Deref;
use super::*; use super::*;
@ -19,7 +18,10 @@ pub enum LayoutNode {
impl LayoutNode { impl LayoutNode {
/// Create a new dynamic node. /// Create a new dynamic node.
pub fn dynamic<T: DynNode>(inner: T) -> Self { pub fn dynamic<T>(inner: T) -> Self
where
T: Layout + Debug + Clone + PartialEq + 'static,
{
Self::Dyn(Dynamic::new(inner)) Self::Dyn(Dynamic::new(inner))
} }
} }
@ -44,33 +46,22 @@ impl Debug for LayoutNode {
} }
} }
/// A wrapper around a boxed node trait object. /// A wrapper around a dynamic layouting node.
/// pub struct Dynamic(Box<dyn Bounds>);
/// _Note_: This is needed because the compiler can't `derive(PartialEq)` for
/// [`LayoutNode`] when directly putting the `Box` in there, see the
/// [Rust Issue].
///
/// [Rust Issue]: https://github.com/rust-lang/rust/issues/31740
pub struct Dynamic(pub Box<dyn DynNode>);
impl Dynamic { impl Dynamic {
/// Wrap a type implementing `DynNode`. /// Create a new instance from any node that satisifies the required bounds.
pub fn new<T: DynNode>(inner: T) -> Self { pub fn new<T>(inner: T) -> Self
where
T: Layout + Debug + Clone + PartialEq + 'static,
{
Self(Box::new(inner)) Self(Box::new(inner))
} }
} }
impl Deref for Dynamic { impl Layout for Dynamic {
type Target = dyn DynNode; fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
self.0.layout(ctx, areas)
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl Debug for Dynamic {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
} }
} }
@ -86,41 +77,33 @@ impl PartialEq for Dynamic {
} }
} }
impl Debug for Dynamic {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<Dynamic> for LayoutNode { impl From<Dynamic> for LayoutNode {
fn from(dynamic: Dynamic) -> Self { fn from(dynamic: Dynamic) -> Self {
Self::Dyn(dynamic) Self::Dyn(dynamic)
} }
} }
/// A dynamic node, which can implement custom layouting behaviour. trait Bounds: Layout + Debug + 'static {
///
/// This trait just combines the requirements for types to qualify as dynamic
/// nodes. The interesting part happens in the inherited trait [`Layout`].
///
/// The trait itself also contains three helper methods to make `Box<dyn
/// DynNode>` able to implement `Clone` and `PartialEq`. However, these are
/// automatically provided by a blanket impl as long as the type in question
/// implements[`Layout`], `Debug`, `PartialEq`, `Clone` and is `'static`.
pub trait DynNode: Debug + Layout + 'static {
/// Convert into a `dyn Any` to enable downcasting.
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn dyn_eq(&self, other: &dyn Bounds) -> bool;
/// Check for equality with another trait object. fn dyn_clone(&self) -> Box<dyn Bounds>;
fn dyn_eq(&self, other: &dyn DynNode) -> bool;
/// Clone into a trait object.
fn dyn_clone(&self) -> Box<dyn DynNode>;
} }
impl<T> DynNode for T impl<T> Bounds for T
where where
T: Debug + Layout + PartialEq + Clone + 'static, T: Layout + Debug + PartialEq + Clone + 'static,
{ {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn dyn_eq(&self, other: &dyn DynNode) -> bool { fn dyn_eq(&self, other: &dyn Bounds) -> bool {
if let Some(other) = other.as_any().downcast_ref::<Self>() { if let Some(other) = other.as_any().downcast_ref::<Self>() {
self == other self == other
} else { } else {
@ -128,7 +111,7 @@ where
} }
} }
fn dyn_clone(&self) -> Box<dyn DynNode> { fn dyn_clone(&self) -> Box<dyn Bounds> {
Box::new(self.clone()) Box::new(self.clone())
} }
} }

View File

@ -109,7 +109,7 @@ pub fn font(mut args: Args, ctx: &mut EvalContext) -> Value {
try_from_match!(FamilyList["family or list of families"] @ span: try_from_match!(FamilyList["family or list of families"] @ span:
Value::Str(v) => Self(vec![v.to_lowercase()]), Value::Str(v) => Self(vec![v.to_lowercase()]),
Value::Dict(v) => Self(Args(v.span_with(span)) Value::Dict(v) => Self(Args(v.with_span(span))
.find_all::<StringLike>() .find_all::<StringLike>()
.map(|s| s.to_lowercase()) .map(|s| s.to_lowercase())
.collect() .collect()

View File

@ -123,10 +123,9 @@ fn heading(p: &mut Parser) -> NodeHeading {
/// Handle a raw block. /// Handle a raw block.
fn raw(p: &mut Parser, token: TokenRaw) -> NodeRaw { fn raw(p: &mut Parser, token: TokenRaw) -> NodeRaw {
let span = p.peek_span();
let raw = resolve::resolve_raw(token.text, token.backticks); let raw = resolve::resolve_raw(token.text, token.backticks);
if !token.terminated { if !token.terminated {
p.diag(error!(span.end, "expected backtick(s)")); p.diag(error!(p.peek_span().end, "expected backtick(s)"));
} }
raw raw
} }
@ -193,7 +192,7 @@ fn bracket_call(p: &mut Parser) -> ExprCall {
while let Some(mut top) = outer.pop() { while let Some(mut top) = outer.pop() {
let span = inner.span; let span = inner.span;
let node = inner.map(|c| SynNode::Expr(Expr::Call(c))); let node = inner.map(|c| SynNode::Expr(Expr::Call(c)));
let expr = Expr::Lit(Lit::Content(vec![node])).span_with(span); let expr = Expr::Lit(Lit::Content(vec![node])).with_span(span);
top.v.args.v.0.push(LitDictEntry { key: None, expr }); top.v.args.v.0.push(LitDictEntry { key: None, expr });
inner = top; inner = top;
} }
@ -213,7 +212,7 @@ fn bracket_subheader(p: &mut Parser) -> ExprCall {
} else { } else {
p.diag_expected(what); p.diag_expected(what);
} }
Ident(String::new()).span_with(start) Ident(String::new()).with_span(start)
}); });
let args = p.span(|p| dict_contents(p).0); let args = p.span(|p| dict_contents(p).0);
@ -247,7 +246,7 @@ fn dict_contents(p: &mut Parser) -> (LitDict, bool) {
if let Some(key) = &entry.key { if let Some(key) = &entry.key {
comma_and_keyless = false; comma_and_keyless = false;
p.deco(Deco::DictKey.span_with(key.span)); p.deco(Deco::Name.with_span(key.span));
} }
dict.0.push(entry); dict.0.push(entry);
@ -286,7 +285,7 @@ fn dict_entry(p: &mut Parser) -> Option<LitDictEntry> {
expr: { expr: {
let start = ident.span.start; let start = ident.span.start;
let call = paren_call(p, ident); let call = paren_call(p, ident);
Expr::Call(call).span_with(start .. p.last_end()) Expr::Call(call).with_span(start .. p.last_end())
}, },
}), }),
@ -335,7 +334,7 @@ fn binops(
op, op,
rhs: Box::new(rhs), rhs: Box::new(rhs),
}); });
lhs = expr.span_with(span); lhs = expr.with_span(span);
} else { } else {
break; break;
} }
@ -361,36 +360,32 @@ fn factor(p: &mut Parser) -> Option<Expr> {
/// Parse a value. /// Parse a value.
fn value(p: &mut Parser) -> Option<Expr> { fn value(p: &mut Parser) -> Option<Expr> {
let start = p.next_start(); let expr = match p.peek() {
Some(match p.eat() {
// Bracketed function call. // Bracketed function call.
Some(Token::LeftBracket) => { Some(Token::LeftBracket) => {
p.jump(start);
let node = p.span(|p| SynNode::Expr(Expr::Call(bracket_call(p)))); let node = p.span(|p| SynNode::Expr(Expr::Call(bracket_call(p))));
Expr::Lit(Lit::Content(vec![node])) return Some(Expr::Lit(Lit::Content(vec![node])));
} }
// Content expression. // Content expression.
Some(Token::LeftBrace) => { Some(Token::LeftBrace) => {
p.jump(start); return Some(Expr::Lit(Lit::Content(content(p))));
Expr::Lit(Lit::Content(content(p)))
} }
// Dictionary or just a parenthesized expression. // Dictionary or just a parenthesized expression.
Some(Token::LeftParen) => { Some(Token::LeftParen) => {
p.jump(start); return Some(parenthesized(p));
parenthesized(p)
} }
// Function or just ident. // Function or just ident.
Some(Token::Ident(id)) => { Some(Token::Ident(id)) => {
p.eat();
let ident = Ident(id.into()); let ident = Ident(id.into());
let after = p.last_end();
if p.peek() == Some(Token::LeftParen) { if p.peek() == Some(Token::LeftParen) {
let name = ident.span_with(start .. after); let name = ident.with_span(p.peek_span());
Expr::Call(paren_call(p, name)) return Some(Expr::Call(paren_call(p, name)));
} else { } else {
Expr::Lit(Lit::Ident(ident)) return Some(Expr::Lit(Lit::Ident(ident)));
} }
} }
@ -400,16 +395,17 @@ fn value(p: &mut Parser) -> Option<Expr> {
Some(Token::Float(f)) => Expr::Lit(Lit::Float(f)), Some(Token::Float(f)) => Expr::Lit(Lit::Float(f)),
Some(Token::Length(val, unit)) => Expr::Lit(Lit::Length(val, unit)), Some(Token::Length(val, unit)) => Expr::Lit(Lit::Length(val, unit)),
Some(Token::Percent(p)) => Expr::Lit(Lit::Percent(p)), Some(Token::Percent(p)) => Expr::Lit(Lit::Percent(p)),
Some(Token::Hex(hex)) => Expr::Lit(Lit::Color(color(p, hex, start))), Some(Token::Hex(hex)) => Expr::Lit(Lit::Color(color(p, hex))),
Some(Token::Str(token)) => Expr::Lit(Lit::Str(str(p, token))), Some(Token::Str(token)) => Expr::Lit(Lit::Str(str(p, token))),
// No value. // No value.
_ => { _ => {
p.jump(start);
p.diag_expected("expression"); p.diag_expected("expression");
return None; return None;
} }
}) };
p.eat();
Some(expr)
} }
// Parse a content value: `{...}`. // Parse a content value: `{...}`.
@ -444,10 +440,10 @@ fn ident(p: &mut Parser) -> Option<Ident> {
} }
/// Parse a color. /// Parse a color.
fn color(p: &mut Parser, hex: &str, start: Pos) -> RgbaColor { fn color(p: &mut Parser, hex: &str) -> RgbaColor {
RgbaColor::from_str(hex).unwrap_or_else(|_| { RgbaColor::from_str(hex).unwrap_or_else(|_| {
// Replace color with black. // Replace color with black.
p.diag(error!(start .. p.last_end(), "invalid color")); p.diag(error!(p.peek_span(), "invalid color"));
RgbaColor::new(0, 0, 0, 255) RgbaColor::new(0, 0, 0, 255)
}) })
} }
@ -455,7 +451,7 @@ fn color(p: &mut Parser, hex: &str, start: Pos) -> RgbaColor {
/// Parse a string. /// Parse a string.
fn str(p: &mut Parser, token: TokenStr) -> String { fn str(p: &mut Parser, token: TokenStr) -> String {
if !token.terminated { if !token.terminated {
p.diag_expected_at("quote", p.last_end()); p.diag_expected_at("quote", p.peek_span().end);
} }
resolve::resolve_string(token.string) resolve::resolve_string(token.string)

View File

@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter};
use super::{Scanner, TokenMode, Tokens}; use super::{Scanner, TokenMode, Tokens};
use crate::diag::Diag; use crate::diag::Diag;
use crate::diag::{Deco, Feedback}; use crate::diag::{Deco, Feedback};
use crate::syntax::{Pos, Span, SpanWith, Spanned, Token}; use crate::syntax::{Pos, Span, Spanned, Token, WithSpan};
/// A convenient token-based parser. /// A convenient token-based parser.
pub struct Parser<'s> { pub struct Parser<'s> {
@ -23,7 +23,7 @@ pub struct Parser<'s> {
/// The stack of open groups. /// The stack of open groups.
groups: Vec<Group>, groups: Vec<Group>,
/// Accumulated feedback. /// Accumulated feedback.
f: Feedback, feedback: Feedback,
} }
impl<'s> Parser<'s> { impl<'s> Parser<'s> {
@ -39,18 +39,18 @@ impl<'s> Parser<'s> {
last_end: Pos::ZERO, last_end: Pos::ZERO,
modes: vec![], modes: vec![],
groups: vec![], groups: vec![],
f: Feedback::new(), feedback: Feedback::new(),
} }
} }
/// Finish parsing and return the accumulated feedback. /// Finish parsing and return the accumulated feedback.
pub fn finish(self) -> Feedback { pub fn finish(self) -> Feedback {
self.f self.feedback
} }
/// Add a diagnostic to the feedback. /// Add a diagnostic to the feedback.
pub fn diag(&mut self, diag: Spanned<Diag>) { pub fn diag(&mut self, diag: Spanned<Diag>) {
self.f.diags.push(diag); self.feedback.diags.push(diag);
} }
/// Eat the next token and add a diagnostic that it is not the expected /// Eat the next token and add a diagnostic that it is not the expected
@ -89,7 +89,7 @@ impl<'s> Parser<'s> {
/// Add a decoration to the feedback. /// Add a decoration to the feedback.
pub fn deco(&mut self, deco: Spanned<Deco>) { pub fn deco(&mut self, deco: Spanned<Deco>) {
self.f.decos.push(deco); self.feedback.decos.push(deco);
} }
/// Update the token mode and push the previous mode onto a stack. /// Update the token mode and push the previous mode onto a stack.
@ -162,7 +162,7 @@ impl<'s> Parser<'s> {
let start = self.next_start; let start = self.next_start;
let output = f(self); let output = f(self);
let end = self.last_end; let end = self.last_end;
output.span_with(start .. end) output.with_span(start .. end)
} }
/// A version of [`span`](Self::span) that works better with options. /// A version of [`span`](Self::span) that works better with options.
@ -251,34 +251,11 @@ impl<'s> Parser<'s> {
self.last_end self.last_end
} }
/// Jump to a position in the source string.
pub fn jump(&mut self, pos: Pos) {
self.tokens.jump(pos);
self.bump();
}
/// Slice a part out of the source string. /// Slice a part out of the source string.
pub fn get(&self, span: impl Into<Span>) -> &'s str { pub fn get(&self, span: impl Into<Span>) -> &'s str {
self.tokens.scanner().get(span.into().to_range()) self.tokens.scanner().get(span.into().to_range())
} }
/// The full source string up to the end of the last token.
pub fn eaten(&self) -> &'s str {
self.tokens.scanner().get(.. self.last_end.to_usize())
}
/// The source string from `start` to the end of the last token.
pub fn eaten_from(&self, start: Pos) -> &'s str {
self.tokens
.scanner()
.get(start.to_usize() .. self.last_end.to_usize())
}
/// The remaining source string after the start of the next token.
pub fn rest(&self) -> &'s str {
self.tokens.scanner().get(self.next_start.to_usize() ..)
}
/// The underlying scanner. /// The underlying scanner.
pub fn scanner(&self) -> Scanner<'s> { pub fn scanner(&self) -> Scanner<'s> {
let mut scanner = self.tokens.scanner().clone(); let mut scanner = self.tokens.scanner().clone();
@ -325,7 +302,8 @@ impl<'s> Parser<'s> {
impl Debug for Parser<'_> { impl Debug for Parser<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Parser({}|{})", self.eaten(), self.rest()) let s = self.scanner();
write!(f, "Parser({}|{})", s.eaten(), s.rest())
} }
} }

View File

@ -102,11 +102,6 @@ impl<'s> Scanner<'s> {
self.peek().map(f).unwrap_or(false) self.peek().map(f).unwrap_or(false)
} }
/// Whether the end of the source string is reached.
pub fn eof(&self) -> bool {
self.index == self.src.len()
}
/// The previous index in the source string. /// The previous index in the source string.
pub fn last_index(&self) -> usize { pub fn last_index(&self) -> usize {
self.src[.. self.index] self.src[.. self.index]
@ -126,11 +121,6 @@ impl<'s> Scanner<'s> {
self.index = index; self.index = index;
} }
/// The full source string.
pub fn src(&self) -> &'s str {
self.src
}
/// Slice a part out of the source string. /// Slice a part out of the source string.
pub fn get<I>(&self, index: I) -> &'s str pub fn get<I>(&self, index: I) -> &'s str
where where

View File

@ -46,11 +46,6 @@ impl<'s> Tokens<'s> {
self.s.index().into() self.s.index().into()
} }
/// Jump to a position in the source string.
pub fn jump(&mut self, pos: Pos) {
self.s.jump(pos.to_usize());
}
/// The underlying scanner. /// The underlying scanner.
pub fn scanner(&self) -> &Scanner<'s> { pub fn scanner(&self) -> &Scanner<'s> {
&self.s &self.s
@ -325,7 +320,9 @@ impl<'s> Tokens<'s> {
} }
// Read the suffix. // Read the suffix.
self.s.eat_while(|c| c == '%' || c.is_ascii_alphanumeric()); if !self.s.eat_if('%') {
self.s.eat_while(|c| c.is_ascii_alphanumeric());
}
// Parse into one of the suitable types. // Parse into one of the suitable types.
let string = self.s.eaten_from(start); let string = self.s.eaten_from(start);
@ -790,5 +787,7 @@ mod tests {
// Test invalid number suffixes. // Test invalid number suffixes.
t!(Header[" /"]: "1foo" => Invalid("1foo")); t!(Header[" /"]: "1foo" => Invalid("1foo"));
t!(Header: "1p%" => Invalid("1p"), Invalid("%"));
t!(Header: "1%%" => Percent(1.0), Invalid("%"));
} }
} }

View File

@ -7,5 +7,5 @@ pub use crate::geom::*;
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::layout::LayoutNode; pub use crate::layout::LayoutNode;
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::syntax::{Span, SpanWith, Spanned, SynTree}; pub use crate::syntax::{Span, Spanned, SynTree, WithSpan};
pub use crate::{error, warning}; pub use crate::{error, warning};

View File

@ -4,12 +4,13 @@ use std::ops::Deref;
use unicode_xid::UnicodeXID; use unicode_xid::UnicodeXID;
/// An identifier as defined by unicode with a few extra permissible characters. /// An Unicode identifier with a few extra permissible characters.
/// ///
/// This is defined as in the [Unicode Standard], but allows additionally /// In addition to what is specified in the [Unicode Standard][uax31], we allow:
/// `-` and `_` as starting and continuing characters. /// - `_` as a starting character,
/// - `_` and `-` as continuing characters.
/// ///
/// [Unicode Standard]: http://www.unicode.org/reports/tr31/ /// [uax31]: http://www.unicode.org/reports/tr31/
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Ident(pub String); pub struct Ident(pub String);

View File

@ -30,16 +30,16 @@ pub enum SynNode {
Expr(Expr), Expr(Expr),
} }
/// A section heading. /// A section heading: `# ...`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct NodeHeading { pub struct NodeHeading {
/// The section depth (how many hashtags minus 1). /// The section depth (numer of hashtags minus 1).
pub level: Spanned<u8>, pub level: Spanned<u8>,
/// The contents of the heading. /// The contents of the heading.
pub contents: SynTree, pub contents: SynTree,
} }
/// A raw block, rendered in monospace with optional syntax highlighting. /// A raw block with optional syntax highlighting: `` `raw` ``.
/// ///
/// Raw blocks start with an arbitrary number of backticks and end with the same /// Raw blocks start with an arbitrary number of backticks and end with the same
/// number of backticks. If you want to include a sequence of backticks in a raw /// number of backticks. If you want to include a sequence of backticks in a raw

View File

@ -12,14 +12,14 @@ thread_local! {
} }
/// Annotate a value with a span. /// Annotate a value with a span.
pub trait SpanWith: Sized { pub trait WithSpan: Sized {
/// Wraps `self` in a `Spanned` with the given span. /// Wraps `self` in a `Spanned` with the given span.
fn span_with(self, span: impl Into<Span>) -> Spanned<Self> { fn with_span(self, span: impl Into<Span>) -> Spanned<Self> {
Spanned::new(self, span) Spanned::new(self, span)
} }
} }
impl<T> SpanWith for T {} impl<T> WithSpan for T {}
/// Span offsetting. /// Span offsetting.
pub trait Offset { pub trait Offset {
@ -81,7 +81,7 @@ impl<T> Spanned<Option<T>> {
/// Swap the spanned and the option. /// Swap the spanned and the option.
pub fn transpose(self) -> Option<Spanned<T>> { pub fn transpose(self) -> Option<Spanned<T>> {
let Spanned { v, span } = self; let Spanned { v, span } = self;
v.map(|v| v.span_with(span)) v.map(|v| v.with_span(span))
} }
} }

View File

@ -22,7 +22,7 @@ use typst::geom::{Length, Point, Sides, Size};
use typst::layout::{BoxLayout, ImageElement, LayoutElement}; use typst::layout::{BoxLayout, ImageElement, LayoutElement};
use typst::parse::{LineMap, Scanner}; use typst::parse::{LineMap, Scanner};
use typst::shaping::Shaped; use typst::shaping::Shaped;
use typst::syntax::{Location, Pos, SpanVec, SpanWith, Spanned}; use typst::syntax::{Location, Pos, SpanVec, Spanned, WithSpan};
use typst::typeset; use typst::typeset;
const TYP_DIR: &str = "typ"; const TYP_DIR: &str = "typ";
@ -212,7 +212,7 @@ fn parse_metadata(src: &str, map: &LineMap) -> (SpanVec<Diag>, bool) {
let mut s = Scanner::new(rest); let mut s = Scanner::new(rest);
let (start, _, end) = (pos(&mut s, map), s.eat_assert('-'), pos(&mut s, map)); let (start, _, end) = (pos(&mut s, map), s.eat_assert('-'), pos(&mut s, map));
diags.push(Diag::new(level, s.rest().trim()).span_with(start .. end)); diags.push(Diag::new(level, s.rest().trim()).with_span(start .. end));
} }
diags.sort(); diags.sort();
@ -303,17 +303,17 @@ fn draw_text(canvas: &mut Canvas, pos: Point, env: &Env, shaped: &Shaped) {
} }
} }
fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, img: &ImageElement) { fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, element: &ImageElement) {
let buf = &env.resources.loaded::<ImageResource>(img.res).buf; let img = &env.resources.loaded::<ImageResource>(element.res);
let mut pixmap = Pixmap::new(buf.width(), buf.height()).unwrap(); let mut pixmap = Pixmap::new(img.buf.width(), img.buf.height()).unwrap();
for ((_, _, src), dest) in buf.pixels().zip(pixmap.pixels_mut()) { for ((_, _, src), dest) in img.buf.pixels().zip(pixmap.pixels_mut()) {
let Rgba([r, g, b, a]) = src; let Rgba([r, g, b, a]) = src;
*dest = ColorU8::from_rgba(r, g, b, a).premultiply(); *dest = ColorU8::from_rgba(r, g, b, a).premultiply();
} }
let view_width = img.size.width.to_pt() as f32; let view_width = element.size.width.to_pt() as f32;
let view_height = img.size.height.to_pt() as f32; let view_height = element.size.height.to_pt() as f32;
let x = pos.x.to_pt() as f32; let x = pos.x.to_pt() as f32;
let y = pos.y.to_pt() as f32; let y = pos.y.to_pt() as f32;