diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-01-26 15:51:13 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-01-26 15:51:13 +0100 |
| commit | 20fb4e7c379b79b84d9884d5f2c89d781c5793e2 (patch) | |
| tree | a1eef90680afa2b43cb1ce0a687c837fd78810e7 /src/syntax/func | |
| parent | 0a087cd28bbee5fcdffbb9d49b0ba9f413ad7f92 (diff) | |
Document everything 📜
Diffstat (limited to 'src/syntax/func')
| -rw-r--r-- | src/syntax/func/keys.rs | 62 | ||||
| -rw-r--r-- | src/syntax/func/maps.rs | 65 | ||||
| -rw-r--r-- | src/syntax/func/mod.rs | 56 | ||||
| -rw-r--r-- | src/syntax/func/values.rs | 106 |
4 files changed, 216 insertions, 73 deletions
diff --git a/src/syntax/func/keys.rs b/src/syntax/func/keys.rs index 116cd4e6..d77447c8 100644 --- a/src/syntax/func/keys.rs +++ b/src/syntax/func/keys.rs @@ -1,3 +1,5 @@ +//! Key types for identifying keyword arguments. + use crate::layout::prelude::*; use super::values::AlignmentValue::{self, *}; use super::*; @@ -6,10 +8,55 @@ use self::AxisKey::*; use self::PaddingKey::*; - +/// Key types are used to extract keyword arguments from +/// [`Objects`](crate::syntax::expr::Object). They represent the key part of a +/// keyword argument. +/// ```typst +/// [func: key=value] +/// ^^^ +/// ``` +/// +/// A key type has an associated output type, which is returned when parsing +/// this key from a string. Most of the time, the output type is simply the key +/// itself, as in the implementation for the [`AxisKey`]: +/// ``` +/// # use typstc::syntax::func::Key; +/// # use typstc::syntax::span::Spanned; +/// # #[derive(Eq, PartialEq)] enum Axis { Horizontal, Vertical, Primary, Secondary } +/// # #[derive(Eq, PartialEq)] enum AxisKey { Specific(Axis), Generic(Axis) } +/// # use Axis::*; +/// # use AxisKey::*; +/// impl Key for AxisKey { +/// type Output = Self; +/// +/// fn parse(key: Spanned<&str>) -> Option<Self::Output> { +/// match key.v { +/// "horizontal" | "h" => Some(Specific(Horizontal)), +/// "vertical" | "v" => Some(Specific(Vertical)), +/// "primary" | "p" => Some(Generic(Primary)), +/// "secondary" | "s" => Some(Generic(Secondary)), +/// _ => None, +/// } +/// } +/// } +/// ``` +/// +/// The axis key would also be useful to identify axes when describing +/// dimensions of objects, as in `width=3cm`, because these are also properties +/// that are stored per axis. However, here the used keyword arguments are +/// actually different (`width` instead of `horizontal`)! Therefore we cannot +/// just use the axis key. +/// +/// To fix this, there is another type [`ExtentKey`] which implements `Key` and +/// has the associated output type axis key. The extent key struct itself has no +/// fields and is only used to extract the axis key. This way, we can specify +/// which argument kind we want without duplicating the type in the background. pub trait Key { + /// The type to parse into. type Output: Eq; + /// Parse a key string into the output type if the string is valid for this + /// key. fn parse(key: Spanned<&str>) -> Option<Self::Output>; } @@ -21,6 +68,7 @@ impl<K: Key> Key for Spanned<K> { } } +/// Implements [`Key`] for types that just need to match on strings. macro_rules! key { ($type:ty, $output:ty, $($($p:pat)|* => $r:expr),* $(,)?) => { impl Key for $type { @@ -36,8 +84,9 @@ macro_rules! key { }; } -/// An argument key which identifies a layouting axis. +/// A key which identifies a layouting axis. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[allow(missing_docs)] pub enum AxisKey { Generic(GenericAxis), Specific(SpecificAxis), @@ -68,6 +117,8 @@ key!(AxisKey, Self, "secondary" | "s" => Generic(Secondary), ); +/// A key which parses into an [`AxisKey`] but uses typical extent keywords +/// instead of axis keywords, e.g. `width` instead of `horizontal`. pub struct ExtentKey; key!(ExtentKey, AxisKey, @@ -77,8 +128,13 @@ key!(ExtentKey, AxisKey, "secondary-size" | "ss" => Generic(Secondary), ); -/// An argument key which identifies an axis, but allows for positional +/// A key which identifies an axis, but alternatively allows for two positional /// arguments with unspecified axes. +/// +/// This type does not implement `Key` in itself since it cannot be parsed from +/// a string. Rather, [`AxisKeys`](AxisKey) and positional arguments should be +/// parsed separately and mapped onto this key, as happening in the +/// [`PosAxisMap`](super::maps::PosAxisMap). #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum PosAxisKey { /// The first positional argument. diff --git a/src/syntax/func/maps.rs b/src/syntax/func/maps.rs index 8941024e..eb4c8394 100644 --- a/src/syntax/func/maps.rs +++ b/src/syntax/func/maps.rs @@ -9,40 +9,46 @@ use super::values::*; use super::*; -/// A deduplicating map type useful for storing possibly redundant arguments. +/// A map which deduplicates redundant arguments. +/// +/// Whenever a duplicate argument is inserted into the map, through the +/// functions `from_iter`, `insert` or `extend` an errors is added to the error +/// list that needs to be passed to those functions. +/// +/// All entries need to have span information to enable the error reporting. #[derive(Debug, Clone, PartialEq)] pub struct DedupMap<K, V> where K: Eq { map: Vec<Spanned<(K, V)>>, } impl<K, V> DedupMap<K, V> where K: Eq { + /// Create a new deduplicating map. pub fn new() -> DedupMap<K, V> { DedupMap { map: vec![] } } + /// Create a new map from an iterator of spanned keys and values. pub fn from_iter<I>(errors: &mut Errors, iter: I) -> DedupMap<K, V> where I: IntoIterator<Item=Spanned<(K, V)>> { let mut map = DedupMap::new(); - for Spanned { v: (key, value), span } in iter.into_iter() { - map.insert(errors, key, value, span); - } + map.extend(errors, iter); map } - /// Add a key-value pair. - pub fn insert(&mut self, errors: &mut Errors, key: K, value: V, span: Span) { - if self.map.iter().any(|e| e.v.0 == key) { - errors.push(err!(span; "duplicate argument")); + /// Add a spanned key-value pair. + pub fn insert(&mut self, errors: &mut Errors, entry: Spanned<(K, V)>) { + if self.map.iter().any(|e| e.v.0 == entry.v.0) { + errors.push(err!(entry.span; "duplicate argument")); } else { - self.map.push(Spanned { v: (key, value), span }); + self.map.push(entry); } } - /// Add multiple key-value pairs. + /// Add multiple spanned key-value pairs. pub fn extend<I>(&mut self, errors: &mut Errors, items: I) where I: IntoIterator<Item=Spanned<(K, V)>> { - for Spanned { v: (k, v), span } in items.into_iter() { - self.insert(errors, k, v, span); + for item in items.into_iter() { + self.insert(errors, item); } } @@ -65,16 +71,15 @@ impl<K, V> DedupMap<K, V> where K: Eq { } /// Create a new map where keys and values are mapped to new keys and - /// values. - /// - /// Returns an error if a new key is duplicate. + /// values. When the mapping introduces new duplicates, errors are + /// generated. pub fn dedup<F, K2, V2>(&self, errors: &mut Errors, mut f: F) -> DedupMap<K2, V2> where F: FnMut(&K, &V) -> (K2, V2), K2: Eq { let mut map = DedupMap::new(); for Spanned { v: (key, value), span } in self.map.iter() { let (key, value) = f(key, value); - map.insert(errors, key, value, *span); + map.insert(errors, Spanned { v: (key, value), span: *span }); } map @@ -86,11 +91,12 @@ impl<K, V> DedupMap<K, V> where K: Eq { } } -/// A map for storing a value for two axes given by keyword arguments. +/// A map for storing a value for axes given by keyword arguments. #[derive(Debug, Clone, PartialEq)] pub struct AxisMap<V>(DedupMap<AxisKey, V>); impl<V: Clone> AxisMap<V> { + /// Parse an axis map from the object. pub fn parse<KT: Key<Output=AxisKey>, VT: Value<Output=V>>( errors: &mut Errors, object: &mut Object, @@ -105,12 +111,13 @@ impl<V: Clone> AxisMap<V> { } } -/// A map for extracting values for two axes that are given through two -/// positional or keyword arguments. +/// A map for storing values for axes that are given through a combination of +/// (two) positional and keyword arguments. #[derive(Debug, Clone, PartialEq)] pub struct PosAxisMap<V>(DedupMap<PosAxisKey, V>); impl<V: Clone> PosAxisMap<V> { + /// Parse a positional/axis map from the function arguments. pub fn parse<KT: Key<Output=AxisKey>, VT: Value<Output=V>>( errors: &mut Errors, args: &mut FuncArgs, @@ -118,8 +125,8 @@ impl<V: Clone> PosAxisMap<V> { let mut map = DedupMap::new(); for &key in &[PosAxisKey::First, PosAxisKey::Second] { - if let Some(value) = args.pos.get::<Spanned<VT>>(errors) { - map.insert(errors, key, value.v, value.span); + if let Some(Spanned { v, span }) = args.pos.get::<Spanned<VT>>(errors) { + map.insert(errors, Spanned { v: (key, v), span }) } } @@ -133,7 +140,8 @@ impl<V: Clone> PosAxisMap<V> { PosAxisMap(map) } - /// Deduplicate from positional or specific to generic axes. + /// Deduplicate from positional arguments and keyword arguments for generic + /// or specific axes to just generic axes. pub fn dedup<F>( &self, errors: &mut Errors, @@ -151,17 +159,19 @@ impl<V: Clone> PosAxisMap<V> { } } -/// A map for extracting padding for a set of specifications given for all -/// sides, opposing sides or single sides. +/// A map for storing padding given for a combination of all sides, opposing +/// sides or single sides. #[derive(Debug, Clone, PartialEq)] pub struct PaddingMap(DedupMap<PaddingKey<AxisKey>, Option<PSize>>); impl PaddingMap { + /// Parse a padding map from the function arguments. pub fn parse(errors: &mut Errors, args: &mut FuncArgs) -> PaddingMap { let mut map = DedupMap::new(); - if let Some(psize) = args.pos.get::<Spanned<Defaultable<PSize>>>(errors) { - map.insert(errors, PaddingKey::All, psize.v, psize.span); + let all = args.pos.get::<Spanned<Defaultable<PSize>>>(errors); + if let Some(Spanned { v, span }) = all { + map.insert(errors, Spanned { v: (PaddingKey::All, v), span }); } let paddings: Vec<_> = args.key @@ -187,8 +197,9 @@ impl PaddingMap { All => All, Both(axis) => Both(axis.to_specific(axes)), Side(axis, alignment) => { + let generic = axis.to_generic(axes); let axis = axis.to_specific(axes); - Side(axis, alignment.to_specific(axes, axis)) + Side(axis, alignment.to_specific(axes, generic)) } }, val) }); diff --git a/src/syntax/func/mod.rs b/src/syntax/func/mod.rs index e66b4d6a..0c5b4447 100644 --- a/src/syntax/func/mod.rs +++ b/src/syntax/func/mod.rs @@ -1,25 +1,38 @@ +//! Primitives for argument parsing in library functions. + use crate::error::{Error, Errors}; use super::expr::{Expr, Ident, Tuple, Object, Pair}; use super::span::{Span, Spanned}; -pub mod maps; -pub mod keys; -pub mod values; +pub_use_mod!(maps); +pub_use_mod!(keys); +pub_use_mod!(values); +/// The parsed header of a function. #[derive(Debug, Clone, PartialEq)] pub struct FuncHeader { + /// The function name, that is: + /// ```typst + /// [box: w=5cm] + /// ^^^ + /// ``` pub name: Spanned<Ident>, + /// The arguments passed to the function. pub args: FuncArgs, } +/// The positional and keyword arguments passed to a function. #[derive(Debug, Clone, PartialEq)] pub struct FuncArgs { + /// The positional arguments. pub pos: Tuple, + /// They keyword arguments. pub key: Object, } impl FuncArgs { + /// Create new empty function arguments. pub fn new() -> FuncArgs { FuncArgs { pos: Tuple::new(), @@ -30,40 +43,32 @@ impl FuncArgs { /// Add an argument. pub fn add(&mut self, arg: FuncArg) { match arg { - FuncArg::Pos(item) => self.add_pos(item), - FuncArg::Key(pair) => self.add_key_pair(pair), + FuncArg::Pos(item) => self.pos.add(item), + FuncArg::Key(pair) => self.key.add(pair), } } - /// Add a positional argument. - pub fn add_pos(&mut self, item: Spanned<Expr>) { - self.pos.add(item); - } - - /// Add a keyword argument. - pub fn add_key(&mut self, key: Spanned<Ident>, value: Spanned<Expr>) { - self.key.add(key, value); - } - - /// Add a keyword argument from an existing pair. - pub fn add_key_pair(&mut self, pair: Pair) { - self.key.add_pair(pair); - } - + /// Iterate over all arguments. pub fn into_iter(self) -> impl Iterator<Item=FuncArg> { self.pos.items.into_iter().map(|item| FuncArg::Pos(item)) .chain(self.key.pairs.into_iter().map(|pair| FuncArg::Key(pair))) } } +/// Either a positional or keyword argument. #[derive(Debug, Clone, PartialEq)] pub enum FuncArg { + /// A positional argument. Pos(Spanned<Expr>), + /// A keyword argument. Key(Pair), } impl FuncArg { - /// The span or the value or combined span of key and value. + /// The full span of this argument. + /// + /// In case of a positional argument this is just the span of the expression + /// and in case of a keyword argument the combined span of key and value. pub fn span(&self) -> Span { match self { FuncArg::Pos(item) => item.span, @@ -72,14 +77,17 @@ impl FuncArg { } } +/// Extra methods on [`Options`](Option) used for argument parsing. pub trait OptionExt: Sized { - fn or_missing(self, errors: &mut Errors, span: Span, what: &str) -> Self; + /// Add an error about a missing argument `arg` with the given span if the + /// option is `None`. + fn or_missing(self, errors: &mut Errors, span: Span, arg: &str) -> Self; } impl<T> OptionExt for Option<T> { - fn or_missing(self, errors: &mut Errors, span: Span, what: &str) -> Self { + fn or_missing(self, errors: &mut Errors, span: Span, arg: &str) -> Self { if self.is_none() { - errors.push(err!(span; "missing argument: {}", what)); + errors.push(err!(span; "missing argument: {}", arg)); } self } diff --git a/src/syntax/func/values.rs b/src/syntax/func/values.rs index a767aef6..515d6a43 100644 --- a/src/syntax/func/values.rs +++ b/src/syntax/func/values.rs @@ -1,3 +1,5 @@ +//! Value types for extracting function arguments. + use std::fmt::{self, Display, Formatter}; use std::marker::PhantomData; use toddle::query::{FontStyle, FontWeight}; @@ -10,9 +12,65 @@ use super::*; use self::AlignmentValue::*; +/// Value types are used to extract the values of positional and keyword +/// arguments from [`Tuples`](crate::syntax::expr::Tuple) and +/// [`Objects`](crate::syntax::expr::Object). They represent the value part of +/// an argument. +/// ```typst +/// [func: value, key=value] +/// ^^^^^ ^^^^^ +/// ``` +/// +/// Similarly to the [`Key`] trait, this trait has an associated output type +/// which the values are parsed into. Most of the time this is just `Self`, as +/// in the implementation for `bool`: +/// ``` +/// # use typstc::err; +/// # use typstc::error::Error; +/// # use typstc::syntax::expr::Expr; +/// # use typstc::syntax::func::Value; +/// # use typstc::syntax::span::Spanned; +/// # struct Bool; /* +/// impl Value for bool { +/// # */ impl Value for Bool { +/// # type Output = bool; /* +/// type Output = Self; +/// # */ +/// +/// fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> { +/// match expr.v { +/// Expr::Bool(b) => Ok(b), +/// other => Err(err!("expected bool, found {}", other.name())), +/// } +/// } +/// } +/// ``` +/// +/// However, sometimes the `Output` type is not just `Self`. For example, there +/// is a value called `Defaultable<V>` which acts as follows: +/// ``` +/// # use typstc::syntax::func::{FuncArgs, Defaultable}; +/// # use typstc::size::Size; +/// # let mut args = FuncArgs::new(); +/// # let mut errors = vec![]; +/// args.key.get::<Defaultable<Size>>(&mut errors, "size"); +/// ``` +/// This will yield. +/// ```typst +/// [func: size=2cm] => Some(Size::cm(2.0)) +/// [func: size=default] => None +/// ``` +/// +/// The type `Defaultable` has no fields and is only used for extracting the +/// option value. This prevents us from having a `Defaultable<V>` type which is +/// essentially simply a bad [`Option`] replacement without the good utility +/// functions. pub trait Value { + /// The type to parse into. type Output; + /// Parse an expression into this value or return an error if the expression + /// is valid for this value type. fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error>; } @@ -25,6 +83,7 @@ impl<V: Value> Value for Spanned<V> { } } +/// Implements [`Value`] for types that just need to match on expressions. macro_rules! value { ($type:ty, $output:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => { impl Value for $type { @@ -57,6 +116,8 @@ value!(ScaleSize, Self, "number or size", Expr::Number(scale) => ScaleSize::Scaled(scale as f32), ); +/// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and returns a +/// String. pub struct StringLike; value!(StringLike, String, "identifier or string", @@ -64,15 +125,18 @@ value!(StringLike, String, "identifier or string", Expr::Str(s) => s, ); -pub struct Defaultable<T>(PhantomData<T>); +/// A value type that matches the string `"default"` or a value type `V` and +/// returns `Option::Some(V::Output)` for a value and `Option::None` for +/// `"default"`. +pub struct Defaultable<V>(PhantomData<V>); -impl<T: Value> Value for Defaultable<T> { - type Output = Option<T::Output>; +impl<V: Value> Value for Defaultable<V> { + type Output = Option<V::Output>; fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> { match expr.v { Expr::Ident(ident) if ident.as_str() == "default" => Ok(None), - _ => T::parse(expr).map(Some) + _ => V::parse(expr).map(Some) } } } @@ -135,8 +199,12 @@ impl Value for Direction { } } +/// A value type that matches identifiers that are valid alignments like +/// `origin` or `right`. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[allow(missing_docs)] pub enum AlignmentValue { + /// A generic alignment. Align(Alignment), Left, Top, @@ -145,26 +213,26 @@ pub enum AlignmentValue { } impl AlignmentValue { - /// The generic axis this alignment corresponds to in the given system of - /// layouting axes. `None` if the alignment is generic. - pub fn axis(self, axes: LayoutAxes) -> Option<GenericAxis> { + /// The specific axis this alignment corresponds to. `None` if the alignment + /// is generic. + pub fn axis(self) -> Option<SpecificAxis> { match self { - Left | Right => Some(Horizontal.to_generic(axes)), - Top | Bottom => Some(Vertical.to_generic(axes)), + Left | Right => Some(Horizontal), + Top | Bottom => Some(Vertical), Align(_) => None, } } - /// The generic version of this alignment in the given system of layouting - /// axes. + /// The generic version of this alignment on the given axis in the given + /// system of layouting axes. /// /// Returns `None` if the alignment is invalid for the given axis. pub fn to_generic(self, axes: LayoutAxes, axis: GenericAxis) -> Option<Alignment> { let specific = axis.to_specific(axes); - let start = match axes.get(axis).is_positive() { - true => Origin, - false => End, - }; + let positive = axes.get(axis).is_positive(); + + // The alignment matching the origin of the positive coordinate direction. + let start = if positive { Origin } else { End }; match (self, specific) { (Align(alignment), _) => Some(alignment), @@ -174,10 +242,10 @@ impl AlignmentValue { } } - /// The specific version of this alignment in the given system of layouting - /// axes. - pub fn to_specific(self, axes: LayoutAxes, axis: SpecificAxis) -> AlignmentValue { - let direction = axes.get_specific(axis); + /// The specific version of this alignment on the given axis in the given + /// system of layouting axes. + pub fn to_specific(self, axes: LayoutAxes, axis: GenericAxis) -> AlignmentValue { + let direction = axes.get(axis); if let Align(alignment) = self { match (direction, alignment) { (LeftToRight, Origin) | (RightToLeft, End) => Left, |
