Finish consistent map and add two further convenience maps 🗺
This commit is contained in:
parent
f5b104d0da
commit
1099330988
@ -29,7 +29,7 @@ macro_rules! function {
|
|||||||
function!(@parse $type $meta | $($rest)*);
|
function!(@parse $type $meta | $($rest)*);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set the metadata to `()` if there is not type definition.
|
// Set the metadata to `()` if there is no type definition.
|
||||||
(@meta $type:ident | $($rest:tt)*) => {
|
(@meta $type:ident | $($rest:tt)*) => {
|
||||||
function!(@parse $type () | $($rest)*);
|
function!(@parse $type () | $($rest)*);
|
||||||
};
|
};
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
//! A deduplicating map.
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::syntax::{Spanned, ParseResult};
|
|
||||||
|
|
||||||
/// A deduplicating map type useful for storing possibly redundant arguments.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct ConsistentMap<K, V> where K: Hash + Eq {
|
|
||||||
map: HashMap<K, V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> ConsistentMap<K, V> where K: Hash + Eq {
|
|
||||||
pub fn new() -> ConsistentMap<K, V> {
|
|
||||||
ConsistentMap { map: HashMap::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a key-value pair.
|
|
||||||
pub fn add(&mut self, key: K, value: V) -> ParseResult<()> {
|
|
||||||
self.map.insert(key, value);
|
|
||||||
// TODO
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a key-value pair if the value is not `None`.
|
|
||||||
pub fn add_opt(&mut self, key: K, value: Option<V>) -> ParseResult<()> {
|
|
||||||
Ok(if let Some(value) = value {
|
|
||||||
self.add(key, value)?;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a key-spanned-value pair the value is not `None`.
|
|
||||||
pub fn add_opt_span(&mut self, key: K, value: Option<Spanned<V>>) -> ParseResult<()> {
|
|
||||||
Ok(if let Some(spanned) = value {
|
|
||||||
self.add(key, spanned.v)?;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Call a function with the value if the key is present.
|
|
||||||
pub fn with<F>(&self, key: K, callback: F) where F: FnOnce(&V) {
|
|
||||||
if let Some(value) = self.map.get(&key) {
|
|
||||||
callback(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new consistent map where keys and values are mapped to new
|
|
||||||
/// keys and values. Returns an error if a new key is duplicate.
|
|
||||||
pub fn dedup<F, K2, V2>(&self, _f: F) -> ParseResult<ConsistentMap<K2, V2>>
|
|
||||||
where F: FnOnce(K, V) -> ParseResult<(K2, V2)>, K2: Hash + Eq {
|
|
||||||
// TODO
|
|
||||||
Ok(ConsistentMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate over the (key, value) pairs.
|
|
||||||
pub fn iter(&self) -> std::collections::hash_map::Iter<'_, K, V> {
|
|
||||||
self.map.iter()
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,13 +8,9 @@ use self::prelude::*;
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
mod map;
|
|
||||||
|
|
||||||
pub use map::ConsistentMap;
|
|
||||||
|
|
||||||
/// Useful imports for creating your own functions.
|
/// Useful imports for creating your own functions.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::map::ConsistentMap;
|
|
||||||
pub use crate::func::{Scope, ParseFunc, LayoutFunc, Command, Commands};
|
pub use crate::func::{Scope, ParseFunc, LayoutFunc, Command, Commands};
|
||||||
pub use crate::layout::{
|
pub use crate::layout::{
|
||||||
layout_tree, Layout, MultiLayout,
|
layout_tree, Layout, MultiLayout,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::func::prelude::*;
|
use crate::func::prelude::*;
|
||||||
use super::keys::*;
|
use super::maps::ConsistentMap;
|
||||||
|
use super::keys::{AxisKey, AlignmentKey};
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
/// `align`: Aligns content along the layouting axes.
|
/// `align`: Aligns content along the layouting axes.
|
||||||
|
@ -1,43 +1,23 @@
|
|||||||
use crate::func::prelude::*;
|
use crate::func::prelude::*;
|
||||||
use super::keys::*;
|
use super::maps::ExtentMap;
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
/// `box`: Layouts content into a box.
|
/// `box`: Layouts content into a box.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Boxed {
|
pub struct Boxed {
|
||||||
body: SyntaxTree,
|
body: SyntaxTree,
|
||||||
map: ConsistentMap<AxisKey, Size>,
|
map: ExtentMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(args, body, ctx) {
|
parse(args, body, ctx) {
|
||||||
let mut map = ConsistentMap::new();
|
|
||||||
|
|
||||||
for arg in args.keys() {
|
|
||||||
let key = match arg.v.key.v.0.as_str() {
|
|
||||||
"width" | "w" => AxisKey::Horizontal,
|
|
||||||
"height" | "h" => AxisKey::Vertical,
|
|
||||||
"primary-size" => AxisKey::Primary,
|
|
||||||
"secondary-size" => AxisKey::Secondary,
|
|
||||||
_ => error!(unexpected_argument),
|
|
||||||
};
|
|
||||||
|
|
||||||
let size = Size::from_expr(arg.v.value)?;
|
|
||||||
map.add(key, size)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Boxed {
|
Boxed {
|
||||||
body: parse!(expected: body, ctx),
|
body: parse!(expected: body, ctx),
|
||||||
map,
|
map: ExtentMap::new(&mut args, false)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, mut ctx) {
|
layout(self, mut ctx) {
|
||||||
let map = self.map.dedup(|key, val| Ok((key.specific(ctx.axes), val)))?;
|
self.map.apply(ctx.axes, &mut ctx.spaces[0].dimensions)?;
|
||||||
|
|
||||||
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)?)]
|
vec![AddMultiple(layout_tree(&self.body, ctx)?)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use crate::func::prelude::*;
|
//! Keys for the consistent maps.
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
macro_rules! kind {
|
macro_rules! kind {
|
||||||
($type:ty, $name:expr, $($patterns:tt)*) => {
|
($type:ty, $name:expr, $($patterns:tt)*) => {
|
||||||
@ -139,7 +141,7 @@ kind!(AlignmentKey, "alignment",
|
|||||||
|
|
||||||
/// An argument key which identifies a margin or padding target.
|
/// An argument key which identifies a margin or padding target.
|
||||||
///
|
///
|
||||||
/// A is the axis type used.
|
/// A is the used axis type.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum PaddingKey<A> {
|
pub enum PaddingKey<A> {
|
||||||
/// All four sides should have the specified padding.
|
/// All four sides should have the specified padding.
|
||||||
@ -150,7 +152,7 @@ pub enum PaddingKey<A> {
|
|||||||
AxisAligned(A, AlignmentKey),
|
AxisAligned(A, AlignmentKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
kind!(PaddingKey<AxisKey>, "axis or anchor",
|
kind!(PaddingKey<AxisKey>, "axis or side",
|
||||||
"horizontal" => PaddingKey::Axis(AxisKey::Horizontal),
|
"horizontal" => PaddingKey::Axis(AxisKey::Horizontal),
|
||||||
"vertical" => PaddingKey::Axis(AxisKey::Vertical),
|
"vertical" => PaddingKey::Axis(AxisKey::Vertical),
|
||||||
"primary" => PaddingKey::Axis(AxisKey::Primary),
|
"primary" => PaddingKey::Axis(AxisKey::Primary),
|
||||||
|
178
src/library/maps.rs
Normal file
178
src/library/maps.rs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
//! Deduplicating maps for argument parsing.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A deduplicating map type useful for storing possibly redundant arguments.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct ConsistentMap<K, V> where K: Hash + Eq {
|
||||||
|
map: HashMap<K, V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> ConsistentMap<K, V> where K: Hash + Eq {
|
||||||
|
pub fn new() -> ConsistentMap<K, V> {
|
||||||
|
ConsistentMap { map: HashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a key-value pair.
|
||||||
|
pub fn add(&mut self, key: K, value: V) -> ParseResult<()> {
|
||||||
|
match self.map.insert(key, value) {
|
||||||
|
Some(_) => error!("duplicate arguments"),
|
||||||
|
None => Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a key-value pair if the value is not `None`.
|
||||||
|
pub fn add_opt(&mut self, key: K, value: Option<V>) -> ParseResult<()> {
|
||||||
|
Ok(if let Some(value) = value {
|
||||||
|
self.add(key, value)?;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a key-spanned-value pair the value is not `None`.
|
||||||
|
pub fn add_opt_span(&mut self, key: K, value: Option<Spanned<V>>) -> ParseResult<()> {
|
||||||
|
Ok(if let Some(spanned) = value {
|
||||||
|
self.add(key, spanned.v)?;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call a function with the value if the key is present.
|
||||||
|
pub fn with<F>(&self, key: K, callback: F) where F: FnOnce(&V) {
|
||||||
|
if let Some(value) = self.map.get(&key) {
|
||||||
|
callback(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new consistent map where keys and values are mapped to new keys
|
||||||
|
/// and values.
|
||||||
|
///
|
||||||
|
/// Returns an error if a new key is duplicate.
|
||||||
|
pub fn dedup<F, K2, V2>(&self, f: F) -> LayoutResult<ConsistentMap<K2, V2>>
|
||||||
|
where
|
||||||
|
F: Fn(&K, &V) -> ParseResult<(K2, V2)>,
|
||||||
|
K2: Hash + Eq
|
||||||
|
{
|
||||||
|
let mut map = ConsistentMap::new();
|
||||||
|
|
||||||
|
for (key, value) in self.map.iter() {
|
||||||
|
let (key, value) = f(key, value)?;
|
||||||
|
map.add(key, value)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over the (key, value) pairs.
|
||||||
|
pub fn iter(&self) -> std::collections::hash_map::Iter<'_, K, V> {
|
||||||
|
self.map.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A map for storing extents along axes.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct ExtentMap(ConsistentMap<AxisKey, Size>);
|
||||||
|
|
||||||
|
impl ExtentMap {
|
||||||
|
/// Parse an extent map from the function args.
|
||||||
|
///
|
||||||
|
/// If `enforce` is true other arguments will create an error, otherwise
|
||||||
|
/// they are left intact.
|
||||||
|
pub fn new(args: &mut FuncArgs, enforce: bool) -> ParseResult<ExtentMap> {
|
||||||
|
let mut map = ConsistentMap::new();
|
||||||
|
|
||||||
|
for arg in args.keys() {
|
||||||
|
let key = match arg.v.key.v.0.as_str() {
|
||||||
|
"width" | "w" => AxisKey::Horizontal,
|
||||||
|
"height" | "h" => AxisKey::Vertical,
|
||||||
|
"primary-size" => AxisKey::Primary,
|
||||||
|
"secondary-size" => AxisKey::Secondary,
|
||||||
|
_ => if enforce {
|
||||||
|
error!("expected dimension")
|
||||||
|
} else {
|
||||||
|
args.add_key(arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let size = Size::from_expr(arg.v.value)?;
|
||||||
|
map.add(key, size)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ExtentMap(map))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map from any axis key to the specific axis kind.
|
||||||
|
pub fn apply(&self, axes: LayoutAxes, dimensions: &mut Size2D) -> LayoutResult<()> {
|
||||||
|
let map = self.0.dedup(|key, &val| Ok((key.specific(axes), val)))?;
|
||||||
|
|
||||||
|
map.with(SpecificAxisKind::Horizontal, |&val| dimensions.x = val);
|
||||||
|
map.with(SpecificAxisKind::Vertical, |&val| dimensions.y = val);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A map for storing padding at sides.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct PaddingMap(ConsistentMap<PaddingKey<AxisKey>, Size>);
|
||||||
|
|
||||||
|
impl PaddingMap {
|
||||||
|
/// Parse an extent map from the function args.
|
||||||
|
///
|
||||||
|
/// If `enforce` is true other arguments will create an error, otherwise
|
||||||
|
/// they are left intact.
|
||||||
|
pub fn new(args: &mut FuncArgs, enforce: bool) -> ParseResult<PaddingMap> {
|
||||||
|
let mut map = ConsistentMap::new();
|
||||||
|
|
||||||
|
map.add_opt_span(PaddingKey::All, args.get_pos_opt::<Size>()?)?;
|
||||||
|
|
||||||
|
for arg in args.keys() {
|
||||||
|
let key = match PaddingKey::from_ident(&arg.v.key) {
|
||||||
|
Ok(key) => key,
|
||||||
|
e => if enforce { e? } else { args.add_key(arg); continue; }
|
||||||
|
};
|
||||||
|
|
||||||
|
let size = Size::from_expr(arg.v.value)?;
|
||||||
|
|
||||||
|
map.add(key, size)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PaddingMap(map))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map from any axis key to the specific axis kind.
|
||||||
|
pub fn apply(&self, axes: LayoutAxes, padding: &mut SizeBox) -> LayoutResult<()> {
|
||||||
|
use PaddingKey::*;
|
||||||
|
|
||||||
|
let map = self.0.dedup(|key, &val| {
|
||||||
|
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))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
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(_, alignment) = key {
|
||||||
|
match alignment {
|
||||||
|
AlignmentKey::Left => padding.left = val,
|
||||||
|
AlignmentKey::Right => padding.right = val,
|
||||||
|
AlignmentKey::Top => padding.top = val,
|
||||||
|
AlignmentKey::Bottom => padding.bottom = val,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -3,11 +3,15 @@
|
|||||||
use crate::func::prelude::*;
|
use crate::func::prelude::*;
|
||||||
use toddle::query::FontClass;
|
use toddle::query::FontClass;
|
||||||
|
|
||||||
|
use keys::*;
|
||||||
|
use maps::*;
|
||||||
|
|
||||||
pub_use_mod!(align);
|
pub_use_mod!(align);
|
||||||
pub_use_mod!(boxed);
|
pub_use_mod!(boxed);
|
||||||
|
|
||||||
mod keys;
|
pub mod maps;
|
||||||
use keys::*;
|
pub mod keys;
|
||||||
|
|
||||||
|
|
||||||
/// Create a scope with all standard functions.
|
/// Create a scope with all standard functions.
|
||||||
pub fn std() -> Scope {
|
pub fn std() -> Scope {
|
||||||
@ -74,22 +78,19 @@ function! {
|
|||||||
/// `page.size`: Set the size of pages.
|
/// `page.size`: Set the size of pages.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct PageSize {
|
pub struct PageSize {
|
||||||
width: Option<Size>,
|
map: ExtentMap,
|
||||||
height: Option<Size>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(args, body) {
|
parse(args, body) {
|
||||||
parse!(forbidden: body);
|
parse!(forbidden: body);
|
||||||
PageSize {
|
PageSize {
|
||||||
width: args.get_key_opt::<Size>("width")?.map(|s| s.v),
|
map: ExtentMap::new(&mut args, true)?,
|
||||||
height: args.get_key_opt::<Size>("height")?.map(|s| s.v),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx) {
|
layout(self, ctx) {
|
||||||
let mut style = ctx.style.page;
|
let mut style = ctx.style.page;
|
||||||
if let Some(width) = self.width { style.dimensions.x = width; }
|
self.map.apply(ctx.axes, &mut style.dimensions)?;
|
||||||
if let Some(height) = self.height { style.dimensions.y = height; }
|
|
||||||
vec![SetPageStyle(style)]
|
vec![SetPageStyle(style)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,58 +99,19 @@ function! {
|
|||||||
/// `page.margins`: Set the margins of pages.
|
/// `page.margins`: Set the margins of pages.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct PageMargins {
|
pub struct PageMargins {
|
||||||
map: ConsistentMap<PaddingKey<AxisKey>, Size>,
|
map: PaddingMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(args, body) {
|
parse(args, body) {
|
||||||
let mut map = ConsistentMap::new();
|
|
||||||
map.add_opt_span(PaddingKey::All, args.get_pos_opt::<Size>()?)?;
|
|
||||||
|
|
||||||
for arg in args.keys() {
|
|
||||||
let key = PaddingKey::from_ident(&arg.v.key)?;
|
|
||||||
let size = Size::from_expr(arg.v.value)?;
|
|
||||||
|
|
||||||
map.add(key, size)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
parse!(forbidden: body);
|
parse!(forbidden: body);
|
||||||
PageMargins { map }
|
PageMargins {
|
||||||
|
map: PaddingMap::new(&mut args, true)?,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx) {
|
layout(self, ctx) {
|
||||||
use PaddingKey::*;
|
|
||||||
|
|
||||||
let axes = ctx.axes;
|
|
||||||
let map = self.map.dedup(|key, val| {
|
|
||||||
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 mut style = ctx.style.page;
|
let mut style = ctx.style.page;
|
||||||
let padding = &mut style.margins;
|
self.map.apply(ctx.axes, &mut style.margins)?;
|
||||||
|
|
||||||
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(_, alignment) = key {
|
|
||||||
match alignment {
|
|
||||||
AlignmentKey::Left => padding.left = val,
|
|
||||||
AlignmentKey::Right => padding.right = val,
|
|
||||||
AlignmentKey::Top => padding.top = val,
|
|
||||||
AlignmentKey::Bottom => padding.bottom = val,
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vec![SetPageStyle(style)]
|
vec![SetPageStyle(style)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Parsing of token streams into syntax trees.
|
//! Parsing of token streams into syntax trees.
|
||||||
|
|
||||||
use crate::TypesetResult;
|
use crate::TypesetResult;
|
||||||
use crate::func::{LayoutFunc, Scope};
|
use crate::func::Scope;
|
||||||
use crate::size::Size;
|
use crate::size::Size;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ impl<'s> Parser<'s> {
|
|||||||
_ => error!("expected arguments or closing bracket"),
|
_ => error!("expected arguments or closing bracket"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let func = FuncCall(self.parse_func_call(name, args)?);
|
let func = self.parse_func_call(name, args)?;
|
||||||
span.end = self.tokens.string_index();
|
span.end = self.tokens.string_index();
|
||||||
|
|
||||||
// Finally this function is parsed to the end.
|
// Finally this function is parsed to the end.
|
||||||
@ -132,16 +132,15 @@ impl<'s> Parser<'s> {
|
|||||||
|
|
||||||
/// Parse the arguments to a function.
|
/// Parse the arguments to a function.
|
||||||
fn parse_func_args(&mut self) -> ParseResult<FuncArgs> {
|
fn parse_func_args(&mut self) -> ParseResult<FuncArgs> {
|
||||||
let mut pos = Vec::new();
|
let mut args = FuncArgs::new();
|
||||||
let mut key = Vec::new();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
self.skip_white();
|
self.skip_white();
|
||||||
|
|
||||||
match self.parse_func_arg()? {
|
match self.parse_func_arg()? {
|
||||||
Some(DynArg::Pos(arg)) => pos.push(arg),
|
Some(DynArg::Pos(arg)) => args.add_pos(arg),
|
||||||
Some(DynArg::Key(arg)) => key.push(arg),
|
Some(DynArg::Key(arg)) => args.add_key(arg),
|
||||||
_ => {},
|
None => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.tokens.next().map(Spanned::value) {
|
match self.tokens.next().map(Spanned::value) {
|
||||||
@ -151,7 +150,7 @@ impl<'s> Parser<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(FuncArgs { pos, key })
|
Ok(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse one argument to a function.
|
/// Parse one argument to a function.
|
||||||
@ -198,8 +197,7 @@ impl<'s> Parser<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a function call.
|
/// Parse a function call.
|
||||||
fn parse_func_call(&mut self, name: Spanned<Ident>, args: FuncArgs)
|
fn parse_func_call(&mut self, name: Spanned<Ident>, args: FuncArgs) -> ParseResult<FuncCall> {
|
||||||
-> ParseResult<Box<dyn LayoutFunc>> {
|
|
||||||
// Now we want to parse this function dynamically.
|
// Now we want to parse this function dynamically.
|
||||||
let parser = self
|
let parser = self
|
||||||
.ctx
|
.ctx
|
||||||
@ -210,7 +208,7 @@ impl<'s> Parser<'s> {
|
|||||||
let has_body = self.tokens.peek().map(Spanned::value) == Some(Token::LeftBracket);
|
let has_body = self.tokens.peek().map(Spanned::value) == Some(Token::LeftBracket);
|
||||||
|
|
||||||
// Do the parsing dependent on whether the function has a body.
|
// Do the parsing dependent on whether the function has a body.
|
||||||
Ok(if has_body {
|
Ok(FuncCall(if has_body {
|
||||||
self.advance();
|
self.advance();
|
||||||
|
|
||||||
// Find out the string which makes the body of this function.
|
// Find out the string which makes the body of this function.
|
||||||
@ -235,7 +233,7 @@ impl<'s> Parser<'s> {
|
|||||||
body
|
body
|
||||||
} else {
|
} else {
|
||||||
parser(args, None, self.ctx)?
|
parser(args, None, self.ctx)?
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression.
|
/// Parse an expression.
|
||||||
|
@ -29,6 +29,8 @@ fn main() {
|
|||||||
fs::create_dir_all(format!("{}/rendered", CACHE_DIR)).unwrap();
|
fs::create_dir_all(format!("{}/rendered", CACHE_DIR)).unwrap();
|
||||||
fs::create_dir_all(format!("{}/pdf", CACHE_DIR)).unwrap();
|
fs::create_dir_all(format!("{}/pdf", CACHE_DIR)).unwrap();
|
||||||
|
|
||||||
|
let mut failed = 0;
|
||||||
|
|
||||||
for entry in fs::read_dir("tests/layouts/").unwrap() {
|
for entry in fs::read_dir("tests/layouts/").unwrap() {
|
||||||
let path = entry.unwrap().path();
|
let path = entry.unwrap().path();
|
||||||
|
|
||||||
@ -49,9 +51,17 @@ fn main() {
|
|||||||
let mut src = String::new();
|
let mut src = String::new();
|
||||||
file.read_to_string(&mut src).unwrap();
|
file.read_to_string(&mut src).unwrap();
|
||||||
|
|
||||||
test(name, &src);
|
if std::panic::catch_unwind(|| test(name, &src)).is_err() {
|
||||||
|
failed += 1;
|
||||||
|
println!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if failed > 0 {
|
||||||
|
println!("{} tests failed.", failed);
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a _PDF_ with a name from the source code.
|
/// Create a _PDF_ with a name from the source code.
|
||||||
|
Loading…
Reference in New Issue
Block a user