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", 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.

View File

@ -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<T>(conv: Conv<T>, diags: &mut SpanVec<Diag>, 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<T>(conv: Conv<T>, slot: &mut Spanned<Value>, 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
}
}

View File

@ -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<Pages>,
/// 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<Document> {
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<Diag>) {
self.f.diags.push(diag);
self.feedback.diags.push(diag);
}
/// Add a decoration to the feedback.
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.
@ -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<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 = ();
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),

View File

@ -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<T> Conv<T> {
impl<T: TryFromValue> TryFromValue for Spanned<T> {
fn try_from_value(value: Spanned<Value>) -> Conv<Self> {
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::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<T: DynNode>(inner: T) -> Self {
pub fn dynamic<T>(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<dyn DynNode>);
/// A wrapper around a dynamic layouting node.
pub struct Dynamic(Box<dyn Bounds>);
impl Dynamic {
/// Wrap a type implementing `DynNode`.
pub fn new<T: DynNode>(inner: T) -> Self {
/// Create a new instance from any node that satisifies the required bounds.
pub fn new<T>(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<Dynamic> 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<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.
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<dyn DynNode>;
fn dyn_eq(&self, other: &dyn Bounds) -> bool;
fn dyn_clone(&self) -> Box<dyn Bounds>;
}
impl<T> DynNode for T
impl<T> 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>() {
self == other
} else {
@ -128,7 +111,7 @@ where
}
}
fn dyn_clone(&self) -> Box<dyn DynNode> {
fn dyn_clone(&self) -> Box<dyn Bounds> {
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:
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>()
.map(|s| s.to_lowercase())
.collect()

View File

@ -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<LitDictEntry> {
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<Expr> {
/// Parse a value.
fn value(p: &mut Parser) -> Option<Expr> {
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<Expr> {
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<Ident> {
}
/// 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)

View File

@ -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<Group>,
/// 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<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
@ -89,7 +89,7 @@ impl<'s> Parser<'s> {
/// Add a decoration to the feedback.
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.
@ -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<Span>) -> &'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())
}
}

View File

@ -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<I>(&self, index: I) -> &'s str
where

View File

@ -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("%"));
}
}

View File

@ -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};

View File

@ -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);

View File

@ -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<u8>,
/// 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

View File

@ -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<Span>) -> Spanned<Self> {
fn with_span(self, span: impl Into<Span>) -> Spanned<Self> {
Spanned::new(self, span)
}
}
impl<T> SpanWith for T {}
impl<T> WithSpan for T {}
/// Span offsetting.
pub trait Offset {
@ -81,7 +81,7 @@ impl<T> Spanned<Option<T>> {
/// Swap the spanned and the option.
pub fn transpose(self) -> Option<Spanned<T>> {
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::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<Diag>, 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::<ImageResource>(img.res).buf;
fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, element: &ImageElement) {
let img = &env.resources.loaded::<ImageResource>(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;