summaryrefslogtreecommitdiff
path: root/src/syntax/func
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-01-26 15:51:13 +0100
committerLaurenz <laurmaedje@gmail.com>2020-01-26 15:51:13 +0100
commit20fb4e7c379b79b84d9884d5f2c89d781c5793e2 (patch)
treea1eef90680afa2b43cb1ce0a687c837fd78810e7 /src/syntax/func
parent0a087cd28bbee5fcdffbb9d49b0ba9f413ad7f92 (diff)
Document everything 📜
Diffstat (limited to 'src/syntax/func')
-rw-r--r--src/syntax/func/keys.rs62
-rw-r--r--src/syntax/func/maps.rs65
-rw-r--r--src/syntax/func/mod.rs56
-rw-r--r--src/syntax/func/values.rs106
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,