From 8cad78481cd52680317032c3bb84cacda5666489 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 1 Jan 2021 17:54:31 +0100 Subject: [PATCH] =?UTF-8?q?A=20few=20small=20improvements=20=E2=99=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/diag.rs | 8 +- src/eval/args.rs | 22 ++++-- src/eval/mod.rs | 170 ++++++++++++++++++++++++------------------- src/eval/value.rs | 8 +- src/layout/node.rs | 71 +++++++----------- src/library/style.rs | 2 +- src/parse/mod.rs | 46 ++++++------ src/parse/parser.rs | 40 +++------- src/parse/scanner.rs | 10 --- src/parse/tokens.rs | 11 ++- src/prelude.rs | 2 +- src/syntax/ident.rs | 9 ++- src/syntax/node.rs | 6 +- src/syntax/span.rs | 8 +- tests/typeset.rs | 16 ++-- 15 files changed, 201 insertions(+), 228 deletions(-) diff --git a/src/diag.rs b/src/diag.rs index c3bef75e7..2872ce5a1 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -104,16 +104,16 @@ impl Display for Level { #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub enum Deco { - /// Emphasized text. - Emph, /// Strong text. Strong, + /// Emphasized text. + Emph, /// A valid, successfully resolved name. Resolved, /// An invalid, unresolved name. Unresolved, - /// A key in a dictionary. - DictKey, + /// A name in a dictionary or argument list. + Name, } /// Construct a diagnostic with [`Error`](Level::Error) level. diff --git a/src/eval/args.rs b/src/eval/args.rs index b379b9751..43c30dafc 100644 --- a/src/eval/args.rs +++ b/src/eval/args.rs @@ -4,7 +4,7 @@ use std::mem; use super::{Conv, EvalContext, RefKey, TryFromValue, Value, ValueDict}; 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 /// functions. @@ -23,7 +23,11 @@ impl Args { { self.0.v.remove(key).and_then(|entry| { 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) { 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 { - ctx.f.diags.push(error!(self.0.span, "missing argument: {}", name)); + ctx.diag(error!(self.0.span, "missing argument: {}", name)); None } } @@ -122,11 +130,11 @@ fn conv_diag(conv: Conv, diags: &mut SpanVec, span: Span) -> Option< match conv { Conv::Ok(t) => Some(t), Conv::Warn(t, warn) => { - diags.push(warn.span_with(span)); + diags.push(warn.with_span(span)); Some(t) } Conv::Err(_, err) => { - diags.push(err.span_with(span)); + diags.push(err.with_span(span)); None } } @@ -137,7 +145,7 @@ fn conv_put_back(conv: Conv, slot: &mut Spanned, span: Span) -> Opt Conv::Ok(t) => Some(t), Conv::Warn(t, _) => Some(t), Conv::Err(v, _) => { - *slot = v.span_with(span); + *slot = v.with_span(span); None } } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index c4da1fe0e..56946210c 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -48,7 +48,7 @@ pub struct EvalContext { /// The active evaluation state. pub state: State, /// The accumulated feedback. - f: Feedback, + feedback: Feedback, /// The finished page runs. runs: Vec, /// The stack of logical groups (paragraphs and such). @@ -71,24 +71,24 @@ impl EvalContext { groups: vec![], inner: vec![], runs: vec![], - f: Feedback::new(), + feedback: Feedback::new(), } } /// Finish evaluation and return the created document. pub fn finish(self) -> Pass { 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. pub fn diag(&mut self, diag: Spanned) { - self.f.diags.push(diag); + self.feedback.diags.push(diag); } /// Add a decoration to the feedback. pub fn deco(&mut self, deco: Spanned) { - self.f.decos.push(deco); + self.feedback.decos.push(deco); } /// 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 /// state. pub fn make_text_node(&self, text: String) -> Text { @@ -293,12 +310,12 @@ struct ParGroup { 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)] pub enum Softness { - /// Soft items can be skipped in some circumstances. + /// A soft item can be skipped in some circumstances. Soft, - /// Hard items are always retained. + /// A hard item is always retained. Hard, } @@ -310,24 +327,35 @@ pub trait Eval { type Output; /// 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> +where + Spanned<&'a T>: Eval, +{ + type Output = as Eval>::Output; + + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + (**self).as_ref().eval(ctx) + } +} + +impl Eval for &[Spanned] { type Output = (); - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(self, ctx: &mut EvalContext) -> Self::Output { for node in self { - node.v.eval(ctx); + node.as_ref().eval(ctx); } } } -impl Eval for SynNode { +impl Eval for Spanned<&SynNode> { type Output = (); - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - match self { + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + match self.v { SynNode::Text(text) => { let node = ctx.make_text_node(text.clone()); ctx.push(node); @@ -340,53 +368,43 @@ impl Eval for SynNode { softness: Softness::Soft, }); } - - SynNode::Linebreak => { - 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::Linebreak => ctx.apply_linebreak(), + SynNode::Parbreak => ctx.apply_parbreak(), SynNode::Strong => ctx.state.font.strong ^= true, SynNode::Emph => ctx.state.font.emph ^= true, - SynNode::Heading(heading) => heading.eval(ctx), - SynNode::Raw(raw) => raw.eval(ctx), + SynNode::Heading(heading) => heading.with_span(self.span).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 = (); - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(self, ctx: &mut EvalContext) -> Self::Output { 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.strong = true; - self.contents.eval(ctx); - SynNode::Parbreak.eval(ctx); + self.v.contents.eval(ctx); + ctx.apply_parbreak(); ctx.state = prev; } } -impl Eval for NodeRaw { +impl Eval for Spanned<&NodeRaw> { 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 families = Rc::make_mut(&mut ctx.state.font.families); 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 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::Spacing(Spacing { amount: line_spacing, @@ -415,24 +433,24 @@ impl Eval for NodeRaw { } } -impl Eval for Expr { +impl Eval for Spanned<&Expr> { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - match self { - Self::Lit(lit) => lit.eval(ctx), - Self::Call(call) => call.eval(ctx), - Self::Unary(unary) => unary.eval(ctx), - Self::Binary(binary) => binary.eval(ctx), + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + match self.v { + Expr::Lit(lit) => lit.with_span(self.span).eval(ctx), + Expr::Call(call) => call.with_span(self.span).eval(ctx), + Expr::Unary(unary) => unary.with_span(self.span).eval(ctx), + Expr::Binary(binary) => binary.with_span(self.span).eval(ctx), } } } -impl Eval for Lit { +impl Eval for Spanned<&Lit> { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - match *self { + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + match *self.v { Lit::Ident(ref v) => Value::Ident(v.clone()), Lit::Bool(v) => Value::Bool(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::Color(v) => Value::Color(Color::Rgba(v)), 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()), } } } -impl Eval for LitDict { +impl Eval for Spanned<&LitDict> { type Output = ValueDict; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(self, ctx: &mut EvalContext) -> Self::Output { let mut dict = ValueDict::new(); - for entry in &self.0 { - let val = entry.expr.v.eval(ctx); - let spanned = val.span_with(entry.expr.span); + for entry in &self.v.0 { + let val = entry.expr.as_ref().eval(ctx); + let spanned = val.with_span(entry.expr.span); if let Some(key) = &entry.key { dict.insert(&key.v, SpannedEntry::new(key.span, spanned)); } else { @@ -466,58 +484,58 @@ impl Eval for LitDict { } } -impl Eval for ExprCall { +impl Eval for Spanned<&ExprCall> { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let name = &self.name.v; - let span = self.name.span; - let dict = self.args.v.eval(ctx); + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + let name = &self.v.name.v; + let span = self.v.name.span; + let dict = self.v.args.as_ref().eval(ctx); if let Some(func) = ctx.state.scope.get(name) { - let args = Args(dict.span_with(self.args.span)); - ctx.f.decos.push(Deco::Resolved.span_with(span)); + let args = Args(dict.with_span(self.v.args.span)); + ctx.feedback.decos.push(Deco::Resolved.with_span(span)); (func.clone())(args, ctx) } else { if !name.is_empty() { 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) } } } -impl Eval for ExprUnary { +impl Eval for Spanned<&ExprUnary> { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let value = self.expr.v.eval(ctx); + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + let value = self.v.expr.eval(ctx); if let Value::Error = value { return Value::Error; } - let span = self.op.span.join(self.expr.span); - match self.op.v { + let span = self.v.op.span.join(self.v.expr.span); + match self.v.op.v { UnOp::Neg => neg(ctx, span, value), } } } -impl Eval for ExprBinary { +impl Eval for Spanned<&ExprBinary> { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let lhs = self.lhs.v.eval(ctx); - let rhs = self.rhs.v.eval(ctx); + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + let lhs = self.v.lhs.eval(ctx); + let rhs = self.v.rhs.eval(ctx); if lhs == Value::Error || rhs == Value::Error { return Value::Error; } - let span = self.lhs.span.join(self.rhs.span); - match self.op.v { + let span = self.v.lhs.span.join(self.v.rhs.span); + match self.v.op.v { BinOp::Add => add(ctx, span, lhs, rhs), BinOp::Sub => sub(ctx, span, lhs, rhs), BinOp::Mul => mul(ctx, span, lhs, rhs), diff --git a/src/eval/value.rs b/src/eval/value.rs index f15ae0c50..a009e8911 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -11,7 +11,7 @@ use crate::color::Color; use crate::diag::Diag; use crate::geom::{Dir, Length, Linear, Relative}; use crate::paper::Paper; -use crate::syntax::{Ident, SpanWith, Spanned, SynTree}; +use crate::syntax::{Ident, Spanned, SynTree, WithSpan}; /// A computational value. #[derive(Clone, PartialEq)] @@ -69,11 +69,11 @@ impl Value { } } -impl Eval for Value { +impl Eval for &Value { type Output = (); /// Evaluate everything contained in this value. - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(self, ctx: &mut EvalContext) -> Self::Output { match self { // Don't print out none values. Value::None => {} @@ -206,7 +206,7 @@ impl Conv { impl TryFromValue for Spanned { fn try_from_value(value: Spanned) -> Conv { 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)) } } diff --git a/src/layout/node.rs b/src/layout/node.rs index f01ded561..447678989 100644 --- a/src/layout/node.rs +++ b/src/layout/node.rs @@ -2,7 +2,6 @@ use std::any::Any; use std::fmt::{self, Debug, Formatter}; -use std::ops::Deref; use super::*; @@ -19,7 +18,10 @@ pub enum LayoutNode { impl LayoutNode { /// Create a new dynamic node. - pub fn dynamic(inner: T) -> Self { + pub fn dynamic(inner: T) -> Self + where + T: Layout + Debug + Clone + PartialEq + 'static, + { Self::Dyn(Dynamic::new(inner)) } } @@ -44,33 +46,22 @@ impl Debug for LayoutNode { } } -/// A wrapper around a boxed node trait object. -/// -/// _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); +/// A wrapper around a dynamic layouting node. +pub struct Dynamic(Box); impl Dynamic { - /// Wrap a type implementing `DynNode`. - pub fn new(inner: T) -> Self { + /// Create a new instance from any node that satisifies the required bounds. + pub fn new(inner: T) -> Self + where + T: Layout + Debug + Clone + PartialEq + 'static, + { Self(Box::new(inner)) } } -impl Deref for Dynamic { - type Target = dyn DynNode; - - 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) +impl Layout for Dynamic { + fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { + self.0.layout(ctx, areas) } } @@ -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 for LayoutNode { fn from(dynamic: Dynamic) -> Self { Self::Dyn(dynamic) } } -/// A dynamic node, which can implement custom layouting behaviour. -/// -/// 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` 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. +trait Bounds: Layout + Debug + 'static { fn as_any(&self) -> &dyn Any; - - /// Check for equality with another trait object. - fn dyn_eq(&self, other: &dyn DynNode) -> bool; - - /// Clone into a trait object. - fn dyn_clone(&self) -> Box; + fn dyn_eq(&self, other: &dyn Bounds) -> bool; + fn dyn_clone(&self) -> Box; } -impl DynNode for T +impl Bounds for T where - T: Debug + Layout + PartialEq + Clone + 'static, + T: Layout + Debug + PartialEq + Clone + 'static, { fn as_any(&self) -> &dyn Any { 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 == other } else { @@ -128,7 +111,7 @@ where } } - fn dyn_clone(&self) -> Box { + fn dyn_clone(&self) -> Box { Box::new(self.clone()) } } diff --git a/src/library/style.rs b/src/library/style.rs index 8571dac69..768380464 100644 --- a/src/library/style.rs +++ b/src/library/style.rs @@ -109,7 +109,7 @@ pub fn font(mut args: Args, ctx: &mut EvalContext) -> Value { try_from_match!(FamilyList["family or list of families"] @ span: 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::() .map(|s| s.to_lowercase()) .collect() diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 99cc9d10f..7880dd7a3 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -123,10 +123,9 @@ fn heading(p: &mut Parser) -> NodeHeading { /// Handle a raw block. fn raw(p: &mut Parser, token: TokenRaw) -> NodeRaw { - let span = p.peek_span(); let raw = resolve::resolve_raw(token.text, token.backticks); if !token.terminated { - p.diag(error!(span.end, "expected backtick(s)")); + p.diag(error!(p.peek_span().end, "expected backtick(s)")); } raw } @@ -193,7 +192,7 @@ fn bracket_call(p: &mut Parser) -> ExprCall { while let Some(mut top) = outer.pop() { let span = inner.span; 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 }); inner = top; } @@ -213,7 +212,7 @@ fn bracket_subheader(p: &mut Parser) -> ExprCall { } else { 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); @@ -247,7 +246,7 @@ fn dict_contents(p: &mut Parser) -> (LitDict, bool) { if let Some(key) = &entry.key { comma_and_keyless = false; - p.deco(Deco::DictKey.span_with(key.span)); + p.deco(Deco::Name.with_span(key.span)); } dict.0.push(entry); @@ -286,7 +285,7 @@ fn dict_entry(p: &mut Parser) -> Option { expr: { let start = ident.span.start; 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, rhs: Box::new(rhs), }); - lhs = expr.span_with(span); + lhs = expr.with_span(span); } else { break; } @@ -361,36 +360,32 @@ fn factor(p: &mut Parser) -> Option { /// Parse a value. fn value(p: &mut Parser) -> Option { - let start = p.next_start(); - Some(match p.eat() { + let expr = match p.peek() { // Bracketed function call. Some(Token::LeftBracket) => { - p.jump(start); 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. Some(Token::LeftBrace) => { - p.jump(start); - Expr::Lit(Lit::Content(content(p))) + return Some(Expr::Lit(Lit::Content(content(p)))); } // Dictionary or just a parenthesized expression. Some(Token::LeftParen) => { - p.jump(start); - parenthesized(p) + return Some(parenthesized(p)); } // Function or just ident. Some(Token::Ident(id)) => { + p.eat(); let ident = Ident(id.into()); - let after = p.last_end(); if p.peek() == Some(Token::LeftParen) { - let name = ident.span_with(start .. after); - Expr::Call(paren_call(p, name)) + let name = ident.with_span(p.peek_span()); + return Some(Expr::Call(paren_call(p, name))); } else { - Expr::Lit(Lit::Ident(ident)) + return Some(Expr::Lit(Lit::Ident(ident))); } } @@ -400,16 +395,17 @@ fn value(p: &mut Parser) -> Option { Some(Token::Float(f)) => Expr::Lit(Lit::Float(f)), Some(Token::Length(val, unit)) => Expr::Lit(Lit::Length(val, unit)), 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))), // No value. _ => { - p.jump(start); p.diag_expected("expression"); return None; } - }) + }; + p.eat(); + Some(expr) } // Parse a content value: `{...}`. @@ -444,10 +440,10 @@ fn ident(p: &mut Parser) -> Option { } /// 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(|_| { // 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) }) } @@ -455,7 +451,7 @@ fn color(p: &mut Parser, hex: &str, start: Pos) -> RgbaColor { /// Parse a string. fn str(p: &mut Parser, token: TokenStr) -> String { 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) diff --git a/src/parse/parser.rs b/src/parse/parser.rs index bf6eb76f9..f6acff6e6 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter}; use super::{Scanner, TokenMode, Tokens}; use crate::diag::Diag; 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. pub struct Parser<'s> { @@ -23,7 +23,7 @@ pub struct Parser<'s> { /// The stack of open groups. groups: Vec, /// Accumulated feedback. - f: Feedback, + feedback: Feedback, } impl<'s> Parser<'s> { @@ -39,18 +39,18 @@ impl<'s> Parser<'s> { last_end: Pos::ZERO, modes: vec![], groups: vec![], - f: Feedback::new(), + feedback: Feedback::new(), } } /// Finish parsing and return the accumulated feedback. pub fn finish(self) -> Feedback { - self.f + self.feedback } /// Add a diagnostic to the feedback. pub fn diag(&mut self, diag: Spanned) { - self.f.diags.push(diag); + self.feedback.diags.push(diag); } /// 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. pub fn deco(&mut self, deco: Spanned) { - self.f.decos.push(deco); + self.feedback.decos.push(deco); } /// 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 output = f(self); 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. @@ -251,34 +251,11 @@ impl<'s> Parser<'s> { 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. pub fn get(&self, span: impl Into) -> &'s str { 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. pub fn scanner(&self) -> Scanner<'s> { let mut scanner = self.tokens.scanner().clone(); @@ -325,7 +302,8 @@ impl<'s> Parser<'s> { impl Debug for Parser<'_> { 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()) } } diff --git a/src/parse/scanner.rs b/src/parse/scanner.rs index c4ba2cfdf..69ad21389 100644 --- a/src/parse/scanner.rs +++ b/src/parse/scanner.rs @@ -102,11 +102,6 @@ impl<'s> Scanner<'s> { 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. pub fn last_index(&self) -> usize { self.src[.. self.index] @@ -126,11 +121,6 @@ impl<'s> Scanner<'s> { self.index = index; } - /// The full source string. - pub fn src(&self) -> &'s str { - self.src - } - /// Slice a part out of the source string. pub fn get(&self, index: I) -> &'s str where diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index c9e159f18..d79197630 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -46,11 +46,6 @@ impl<'s> Tokens<'s> { 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. pub fn scanner(&self) -> &Scanner<'s> { &self.s @@ -325,7 +320,9 @@ impl<'s> Tokens<'s> { } // 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. let string = self.s.eaten_from(start); @@ -790,5 +787,7 @@ mod tests { // Test invalid number suffixes. t!(Header[" /"]: "1foo" => Invalid("1foo")); + t!(Header: "1p%" => Invalid("1p"), Invalid("%")); + t!(Header: "1%%" => Percent(1.0), Invalid("%")); } } diff --git a/src/prelude.rs b/src/prelude.rs index 0c28dd202..b9cf561d4 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -7,5 +7,5 @@ pub use crate::geom::*; #[doc(no_inline)] pub use crate::layout::LayoutNode; #[doc(no_inline)] -pub use crate::syntax::{Span, SpanWith, Spanned, SynTree}; +pub use crate::syntax::{Span, Spanned, SynTree, WithSpan}; pub use crate::{error, warning}; diff --git a/src/syntax/ident.rs b/src/syntax/ident.rs index 55f97f950..4f3668c06 100644 --- a/src/syntax/ident.rs +++ b/src/syntax/ident.rs @@ -4,12 +4,13 @@ use std::ops::Deref; 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 -/// `-` and `_` as starting and continuing characters. +/// In addition to what is specified in the [Unicode Standard][uax31], we allow: +/// - `_` 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)] pub struct Ident(pub String); diff --git a/src/syntax/node.rs b/src/syntax/node.rs index 866c935e4..b7691a70e 100644 --- a/src/syntax/node.rs +++ b/src/syntax/node.rs @@ -30,16 +30,16 @@ pub enum SynNode { Expr(Expr), } -/// A section heading. +/// A section heading: `# ...`. #[derive(Debug, Clone, PartialEq)] pub struct NodeHeading { - /// The section depth (how many hashtags minus 1). + /// The section depth (numer of hashtags minus 1). pub level: Spanned, /// The contents of the heading. 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 /// number of backticks. If you want to include a sequence of backticks in a raw diff --git a/src/syntax/span.rs b/src/syntax/span.rs index 820de3b34..200083547 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -12,14 +12,14 @@ thread_local! { } /// Annotate a value with a span. -pub trait SpanWith: Sized { +pub trait WithSpan: Sized { /// Wraps `self` in a `Spanned` with the given span. - fn span_with(self, span: impl Into) -> Spanned { + fn with_span(self, span: impl Into) -> Spanned { Spanned::new(self, span) } } -impl SpanWith for T {} +impl WithSpan for T {} /// Span offsetting. pub trait Offset { @@ -81,7 +81,7 @@ impl Spanned> { /// Swap the spanned and the option. pub fn transpose(self) -> Option> { let Spanned { v, span } = self; - v.map(|v| v.span_with(span)) + v.map(|v| v.with_span(span)) } } diff --git a/tests/typeset.rs b/tests/typeset.rs index ec2ad62d5..4f76d4675 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -22,7 +22,7 @@ use typst::geom::{Length, Point, Sides, Size}; use typst::layout::{BoxLayout, ImageElement, LayoutElement}; use typst::parse::{LineMap, Scanner}; use typst::shaping::Shaped; -use typst::syntax::{Location, Pos, SpanVec, SpanWith, Spanned}; +use typst::syntax::{Location, Pos, SpanVec, Spanned, WithSpan}; use typst::typeset; const TYP_DIR: &str = "typ"; @@ -212,7 +212,7 @@ fn parse_metadata(src: &str, map: &LineMap) -> (SpanVec, bool) { let mut s = Scanner::new(rest); 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(); @@ -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) { - let buf = &env.resources.loaded::(img.res).buf; +fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, element: &ImageElement) { + let img = &env.resources.loaded::(element.res); - let mut pixmap = Pixmap::new(buf.width(), buf.height()).unwrap(); - for ((_, _, src), dest) in buf.pixels().zip(pixmap.pixels_mut()) { + let mut pixmap = Pixmap::new(img.buf.width(), img.buf.height()).unwrap(); + for ((_, _, src), dest) in img.buf.pixels().zip(pixmap.pixels_mut()) { let Rgba([r, g, b, a]) = src; *dest = ColorU8::from_rgba(r, g, b, a).premultiply(); } - let view_width = img.size.width.to_pt() as f32; - let view_height = img.size.height.to_pt() as f32; + let view_width = element.size.width.to_pt() as f32; + let view_height = element.size.height.to_pt() as f32; let x = pos.x.to_pt() as f32; let y = pos.y.to_pt() as f32;