summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
Diffstat (limited to 'src/library')
-rw-r--r--src/library/align.rs37
-rw-r--r--src/library/boxed.rs21
-rw-r--r--src/library/keys.rs172
-rw-r--r--src/library/mod.rs104
4 files changed, 229 insertions, 105 deletions
diff --git a/src/library/align.rs b/src/library/align.rs
index 417d8f07..eea25dfa 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -1,29 +1,25 @@
use crate::func::prelude::*;
+use super::keys::*;
function! {
/// `align`: Aligns content along the layouting axes.
#[derive(Debug, PartialEq)]
pub struct Align {
body: Option<SyntaxTree>,
- map: ArgMap<Key, AlignmentKey>,
+ map: ConsistentMap<Key, AlignmentKey>,
}
parse(args, body, ctx) {
- let mut map = ArgMap::new();
- map.put(Key::First, args.get_pos_opt::<ArgIdent>()?)?;
- map.put(Key::Second, args.get_pos_opt::<ArgIdent>()?)?;
+ let mut map = ConsistentMap::new();
+
+ map.add_opt_span(Key::First, args.get_pos_opt::<AlignmentKey>()?)?;
+ map.add_opt_span(Key::Second, args.get_pos_opt::<AlignmentKey>()?)?;
for arg in args.keys() {
- let key = match arg.val.0.val {
- "horizontal" => Key::Axis(AxisKey::Horizontal),
- "vertical" => Key::Axis(AxisKey::Vertical),
- "primary" => Key::Axis(AxisKey::Primary),
- "secondary" => Key::Axis(AxisKey::Secondary),
- _ => error!(unexpected_argument),
- };
+ let axis = AxisKey::from_ident(&arg.v.key)?;
+ let value = AlignmentKey::from_expr(arg.v.value)?;
- let value = AlignmentKey::parse(arg.val.1.val)?;
- map.add(key, value);
+ map.add(Key::Axis(axis), value)?;
}
Align {
@@ -34,24 +30,23 @@ function! {
layout(self, mut ctx) {
let axes = ctx.axes;
- let basic = axes.primary.is_horizontal();
- let map = self.map.dedup(|key, val| {
+ let map = self.map.dedup(|key, alignment| {
let axis = match key {
- Key::First => val.axis(axes, GenericAxisKind::Primary),
- Key::Second => val.axis(axes, GenericAxisKind::Secondary),
+ Key::First => alignment.axis(axes, GenericAxisKind::Primary),
+ Key::Second => alignment.axis(axes, GenericAxisKind::Secondary),
Key::Axis(AxisKey::Primary) => GenericAxisKind::Primary,
Key::Axis(AxisKey::Secondary) => GenericAxisKind::Secondary,
Key::Axis(AxisKey::Horizontal) => axes.horizontal(),
Key::Axis(AxisKey::Vertical) => axes.vertical(),
};
- let alignment = val.generic(axes, axis)?;
- Ok((key, alignment))
+ let alignment = alignment.generic(axes, axis)?;
+ Ok((axis, alignment))
})?;
- map.with(GenericAxisKind::Primary, |val| ctx.alignment.primary = val);
- map.with(GenericAxisKind::Secondary, |val| ctx.alignment.secondary = val);
+ map.with(GenericAxisKind::Primary, |&val| ctx.alignment.primary = val);
+ map.with(GenericAxisKind::Secondary, |&val| ctx.alignment.secondary = val);
match &self.body {
Some(body) => vec![AddMultiple(layout_tree(&body, ctx)?)],
diff --git a/src/library/boxed.rs b/src/library/boxed.rs
index a2df45e3..ef5ae24e 100644
--- a/src/library/boxed.rs
+++ b/src/library/boxed.rs
@@ -1,18 +1,19 @@
use crate::func::prelude::*;
+use super::keys::*;
function! {
/// `box`: Layouts content into a box.
#[derive(Debug, PartialEq)]
pub struct Boxed {
body: SyntaxTree,
- map: ArgMap<AxisKey, Size>,
+ map: ConsistentMap<AxisKey, Size>,
}
parse(args, body, ctx) {
- let mut map = ArgMap::new();
+ let mut map = ConsistentMap::new();
for arg in args.keys() {
- let key = match arg.val.0.val {
+ let key = match arg.v.key.v.0.as_str() {
"width" | "w" => AxisKey::Horizontal,
"height" | "h" => AxisKey::Vertical,
"primary-size" => AxisKey::Primary,
@@ -20,8 +21,8 @@ function! {
_ => error!(unexpected_argument),
};
- let size = ArgParser::convert::<ArgSize>(arg.val.1.val)?;
- map.add(key, size);
+ let size = Size::from_expr(arg.v.value)?;
+ map.add(key, size)?;
}
Boxed {
@@ -31,13 +32,11 @@ function! {
}
layout(self, mut ctx) {
- let map = self.map.dedup(|key, val| {
- Ok((key.specific(ctx.axes), val))
- });
+ let map = self.map.dedup(|key, val| Ok((key.specific(ctx.axes), val)))?;
- let mut dimensions = &mut ctx.spaces[0].dimensions;
- map.with(AxisKey::Horizontal, |val| dimensions.x = val);
- map.with(AxisKey::Vertical, |val| dimensions.y = val);
+ let dimensions = &mut ctx.spaces[0].dimensions;
+ map.with(SpecificAxisKind::Horizontal, |&val| dimensions.x = val);
+ map.with(SpecificAxisKind::Vertical, |&val| dimensions.y = val);
vec![AddMultiple(layout_tree(&self.body, ctx)?)]
}
diff --git a/src/library/keys.rs b/src/library/keys.rs
new file mode 100644
index 00000000..df658027
--- /dev/null
+++ b/src/library/keys.rs
@@ -0,0 +1,172 @@
+use crate::func::prelude::*;
+
+macro_rules! kind {
+ ($type:ty, $name:expr, $($patterns:tt)*) => {
+ impl $type {
+ /// Parse this key from an identifier.
+ pub fn from_ident(ident: &Spanned<Ident>) -> ParseResult<Self> {
+ Ok(match ident.v.0.as_str() {
+ $($patterns)*
+ _ => error!("expected {}", <Self as ExpressionKind>::NAME),
+ })
+ }
+ }
+
+ impl ExpressionKind for $type {
+ const NAME: &'static str = $name;
+
+ fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self> {
+ if let Expression::Ident(ident) = expr.v {
+ Self::from_ident(&Spanned::new(ident, expr.span))
+ } else {
+ error!("expected {}", Self::NAME);
+ }
+ }
+ }
+ };
+}
+
+/// An argument key which identifies a layouting axis.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum AxisKey {
+ Primary,
+ Secondary,
+ Vertical,
+ Horizontal,
+}
+
+impl AxisKey {
+ /// The generic version of this axis key in the given system of axes.
+ pub fn generic(&self, axes: LayoutAxes) -> GenericAxisKind {
+ match self {
+ AxisKey::Primary => GenericAxisKind::Primary,
+ AxisKey::Secondary => GenericAxisKind::Secondary,
+ AxisKey::Vertical => axes.vertical(),
+ AxisKey::Horizontal => axes.horizontal(),
+ }
+ }
+
+ /// The specific version of this axis key in the given system of axes.
+ pub fn specific(&self, axes: LayoutAxes) -> SpecificAxisKind {
+ match self {
+ AxisKey::Primary => axes.primary(),
+ AxisKey::Secondary => axes.secondary(),
+ AxisKey::Vertical => SpecificAxisKind::Vertical,
+ AxisKey::Horizontal => SpecificAxisKind::Horizontal,
+ }
+ }
+}
+
+kind!(AxisKey, "axis",
+ "horizontal" => AxisKey::Horizontal,
+ "vertical" => AxisKey::Vertical,
+ "primary" => AxisKey::Primary,
+ "secondary" => AxisKey::Secondary,
+);
+
+/// An argument key which identifies a target alignment.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum AlignmentKey {
+ Left,
+ Top,
+ Right,
+ Bottom,
+ Origin,
+ Center,
+ End,
+}
+
+impl AlignmentKey {
+ /// The generic axis this alignment key corresopnds to in the given system
+ /// of layouting axes. Falls back to `default` if the alignment is generic.
+ pub fn axis(&self, axes: LayoutAxes, default: GenericAxisKind) -> GenericAxisKind {
+ use AlignmentKey::*;
+ match self {
+ Origin | Center | End => default,
+ Left | Right => axes.horizontal(),
+ Top | Bottom => axes.vertical(),
+ }
+ }
+
+ /// The generic version of this alignment in the given system of layouting
+ /// axes. Returns an error if the alignment is invalid for the given axis.
+ pub fn generic(&self, axes: LayoutAxes, axis: GenericAxisKind) -> LayoutResult<Alignment> {
+ use AlignmentKey::*;
+
+ let horizontal = axis == axes.horizontal();
+ Ok(match self {
+ Origin => Alignment::Origin,
+ Center => Alignment::Center,
+ End => Alignment::End,
+ Left if horizontal => axes.left(),
+ Right if horizontal => axes.right(),
+ Top if !horizontal => axes.top(),
+ Bottom if !horizontal => axes.bottom(),
+ _ => error!(
+ "invalid alignment `{}` for {} axis",
+ format!("{:?}", self).to_lowercase(),
+ format!("{:?}", axis).to_lowercase()
+ )
+ })
+ }
+
+ /// The specific version of this alignment in the given system of layouting
+ /// axes.
+ pub fn specific(&self, axes: LayoutAxes, axis: SpecificAxisKind) -> AlignmentKey {
+ use AlignmentKey::*;
+ use SpecificAxisKind::*;
+
+ let positive = axes.get_specific(axis).is_positive();
+ match (self, axis, positive) {
+ (Origin, Horizontal, true) | (End, Horizontal, false) => Left,
+ (End, Horizontal, true) | (Origin, Horizontal, false) => Right,
+ (Origin, Vertical, true) | (End, Vertical, false) => Top,
+ (End, Vertical, true) | (Origin, Vertical, false) => Bottom,
+ _ => *self,
+ }
+ }
+}
+
+kind!(AlignmentKey, "alignment",
+ "left" => AlignmentKey::Left,
+ "top" => AlignmentKey::Top,
+ "right" => AlignmentKey::Right,
+ "bottom" => AlignmentKey::Bottom,
+ "origin" => AlignmentKey::Origin,
+ "center" => AlignmentKey::Center,
+ "end" => AlignmentKey::End,
+);
+
+/// An argument key which identifies a margin or padding target.
+///
+/// A is the axis type used.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum PaddingKey<A> {
+ /// All four sides should have the specified padding.
+ All,
+ /// Both sides of the given axis should have the specified padding.
+ Axis(A),
+ /// Only the given side of the given axis should have the specified padding.
+ AxisAligned(A, AlignmentKey),
+}
+
+kind!(PaddingKey<AxisKey>, "axis or anchor",
+ "horizontal" => PaddingKey::Axis(AxisKey::Horizontal),
+ "vertical" => PaddingKey::Axis(AxisKey::Vertical),
+ "primary" => PaddingKey::Axis(AxisKey::Primary),
+ "secondary" => PaddingKey::Axis(AxisKey::Secondary),
+
+ "left" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Left),
+ "right" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Right),
+ "top" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Top),
+ "bottom" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Bottom),
+
+ "primary-origin" => PaddingKey::AxisAligned(AxisKey::Primary, AlignmentKey::Origin),
+ "primary-end" => PaddingKey::AxisAligned(AxisKey::Primary, AlignmentKey::End),
+ "secondary-origin" => PaddingKey::AxisAligned(AxisKey::Secondary, AlignmentKey::Origin),
+ "secondary-end" => PaddingKey::AxisAligned(AxisKey::Secondary, AlignmentKey::End),
+ "horizontal-origin" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Origin),
+ "horizontal-end" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::End),
+ "vertical-origin" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Origin),
+ "vertical-end" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::End),
+);
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 02af0d83..f25c6397 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -6,6 +6,9 @@ use toddle::query::FontClass;
pub_use_mod!(align);
pub_use_mod!(boxed);
+mod keys;
+use keys::*;
+
/// Create a scope with all standard functions.
pub fn std() -> Scope {
let mut std = Scope::new();
@@ -33,7 +36,7 @@ pub fn std() -> Scope {
("italic", FontClass::Italic),
("mono", FontClass::Monospace),
] {
- std.add_with_metadata::<StyleChange, FontClass>(name, *class);
+ std.add_with_metadata::<StyleChange, FontClass>(name, class.clone());
}
std
@@ -80,8 +83,8 @@ function! {
parse(args, body) {
parse!(forbidden: body);
PageSize {
- width: args.get_key_opt::<ArgSize>("width")?.map(|a| a.val),
- height: args.get_key_opt::<ArgSize>("height")?.map(|a| a.val),
+ width: args.get_key_opt::<Size>("width")?.map(|s| s.v),
+ height: args.get_key_opt::<Size>("height")?.map(|s| s.v),
}
}
@@ -97,42 +100,18 @@ function! {
/// `page.margins`: Set the margins of pages.
#[derive(Debug, PartialEq)]
pub struct PageMargins {
- map: ArgMap<PaddingKey, Size>,
+ map: ConsistentMap<PaddingKey<AxisKey>, Size>,
}
parse(args, body) {
- use PaddingKey::*;
- use AlignmentKey::*;
-
- let mut map = ArgMap::new();
- map.add_opt(All, args.get_pos_opt::<ArgSize>()?);
+ let mut map = ConsistentMap::new();
+ map.add_opt_span(PaddingKey::All, args.get_pos_opt::<Size>()?)?;
for arg in args.keys() {
- let key = match arg.val.0.val {
- "horizontal" => Axis(AxisKey::Horizontal),
- "vertical" => Axis(AxisKey::Vertical),
- "primary" => Axis(AxisKey::Primary),
- "secondary" => Axis(AxisKey::Secondary),
-
- "left" => AxisAligned(AxisKey::Horizontal, Left),
- "right" => AxisAligned(AxisKey::Horizontal, Right),
- "top" => AxisAligned(AxisKey::Vertical, Top),
- "bottom" => AxisAligned(AxisKey::Vertical, Bottom),
-
- "primary-origin" => AxisAligned(AxisKey::Primary, Origin),
- "primary-end" => AxisAligned(AxisKey::Primary, End),
- "secondary-origin" => AxisAligned(AxisKey::Secondary, Origin),
- "secondary-end" => AxisAligned(AxisKey::Secondary, End),
- "horizontal-origin" => AxisAligned(AxisKey::Horizontal, Origin),
- "horizontal-end" => AxisAligned(AxisKey::Horizontal, End),
- "vertical-origin" => AxisAligned(AxisKey::Vertical, Origin),
- "vertical-end" => AxisAligned(AxisKey::Vertical, End),
-
- _ => error!(unexpected_argument),
- };
-
- let size = ArgParser::convert::<ArgSize>(arg.val.1.val)?;
- map.add(key, size);
+ let key = PaddingKey::from_ident(&arg.v.key)?;
+ let size = Size::from_expr(arg.v.value)?;
+
+ map.add(key, size)?;
}
parse!(forbidden: body);
@@ -144,25 +123,25 @@ function! {
let axes = ctx.axes;
let map = self.map.dedup(|key, val| {
- match key {
+ Ok((match key {
All => All,
Axis(axis) => Axis(axis.specific(axes)),
AxisAligned(axis, alignment) => {
let axis = axis.specific(axes);
AxisAligned(axis, alignment.specific(axes, axis))
}
- }
- });
+ }, val))
+ })?;
- let style = ctx.style.page;
+ let mut style = ctx.style.page;
let padding = &mut style.margins;
- map.with(All, |val| padding.set_all(val));
- map.with(Axis(AxisKey::Horizontal), |val| padding.set_horizontal(val));
- map.with(Axis(AxisKey::Vertical), |val| padding.set_vertical(val));
+ map.with(All, |&val| padding.set_all(val));
+ map.with(Axis(SpecificAxisKind::Horizontal), |&val| padding.set_horizontal(val));
+ map.with(Axis(SpecificAxisKind::Vertical), |&val| padding.set_vertical(val));
- for (key, val) in map.iter() {
- if let AxisAligned(axis, alignment) = key {
+ for (key, &val) in map.iter() {
+ if let AxisAligned(_, alignment) = key {
match alignment {
AlignmentKey::Left => padding.left = val,
AlignmentKey::Right => padding.right = val,
@@ -182,7 +161,7 @@ function! {
#[derive(Debug, PartialEq)]
pub struct Spacing {
axis: AxisKey,
- spacing: SpacingValue,
+ spacing: FSize,
}
type Meta = Option<AxisKey>;
@@ -191,19 +170,14 @@ function! {
let spacing = if let Some(axis) = meta {
Spacing {
axis,
- spacing: SpacingValue::from_expr(args.get_pos::<ArgExpr>()?)?,
+ spacing: FSize::from_expr(args.get_pos::<Expression>()?)?,
}
} else {
if let Some(arg) = args.get_key_next() {
- let axis = match arg.val.0.val {
- "horizontal" => AxisKey::Horizontal,
- "vertical" => AxisKey::Vertical,
- "primary" => AxisKey::Primary,
- "secondary" => AxisKey::Secondary,
- _ => error!(unexpected_argument),
- };
-
- let spacing = SpacingValue::from_expr(arg.val.1.val)?;
+ let axis = AxisKey::from_ident(&arg.v.key)
+ .map_err(|_| error!(@unexpected_argument))?;
+
+ let spacing = FSize::from_expr(arg.v.value)?;
Spacing { axis, spacing }
} else {
error!("expected axis and expression")
@@ -217,30 +191,14 @@ function! {
layout(self, ctx) {
let axis = self.axis.generic(ctx.axes);
let spacing = match self.spacing {
- SpacingValue::Absolute(s) => s,
- SpacingValue::Relative(f) => f * ctx.style.text.font_size,
+ FSize::Absolute(size) => size,
+ FSize::Scaled(scale) => scale * ctx.style.text.font_size,
};
vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
}
}
-#[derive(Debug, PartialEq)]
-enum SpacingValue {
- Absolute(Size),
- Relative(f32),
-}
-
-impl SpacingValue {
- fn from_expr(expr: Spanned<&Expression>) -> ParseResult<SpacingValue> {
- Ok(match expr.val {
- Expression::Size(s) => SpacingValue::Absolute(*s),
- Expression::Num(f) => SpacingValue::Relative(*f as f32),
- _ => error!("invalid spacing: expected size or number"),
- })
- }
-}
-
function! {
/// Sets text with a different style.
#[derive(Debug, PartialEq)]
@@ -260,7 +218,7 @@ function! {
layout(self, ctx) {
let mut style = ctx.style.text.clone();
- style.toggle_class(self.class);
+ style.toggle_class(self.class.clone());
match &self.body {
Some(body) => vec![