summaryrefslogtreecommitdiff
path: root/src/func
diff options
context:
space:
mode:
Diffstat (limited to 'src/func')
-rw-r--r--src/func/args.rs215
-rw-r--r--src/func/macros.rs22
-rw-r--r--src/func/map.rs59
-rw-r--r--src/func/mod.rs36
4 files changed, 95 insertions, 237 deletions
diff --git a/src/func/args.rs b/src/func/args.rs
deleted file mode 100644
index d1d49b6a..00000000
--- a/src/func/args.rs
+++ /dev/null
@@ -1,215 +0,0 @@
-//! Parsing, storing and deduplication of function arguments.
-
-use super::prelude::*;
-use Expression::*;
-
-/// Provides a convenient interface to parse the arguments to a function.
-pub struct ArgParser<'a> {
- args: &'a FuncArgs,
- positional_index: usize,
-}
-
-impl<'a> ArgParser<'a> {
- pub fn new(args: &'a FuncArgs) -> ArgParser<'a> {
- ArgParser {
- args,
- positional_index: 0,
- }
- }
-
- /// Get the next positional argument of the given type.
- ///
- /// If there are no more arguments or the type is wrong,
- /// this will return an error.
- pub fn get_pos<T>(&mut self) -> ParseResult<Spanned<T::Output>>
- where T: Argument<'a> {
- Self::expected(self.get_pos_opt::<T>()?)
- }
-
- /// Get the next positional argument if there is any.
- ///
- /// If the argument is of the wrong type, this will return an error.
- pub fn get_pos_opt<T>(&mut self) -> ParseResult<Option<Spanned<T::Output>>>
- where T: Argument<'a> {
- let arg = self.args.positional
- .get(self.positional_index)
- .map(T::from_expr)
- .transpose();
-
- if let Ok(Some(_)) = arg {
- self.positional_index += 1;
- }
-
- arg
- }
-
- /// Get a keyword argument with the given key and type.
- pub fn get_key<T>(&mut self, key: &str) -> ParseResult<Spanned<T::Output>>
- where T: Argument<'a> {
- Self::expected(self.get_key_opt::<T>(key)?)
- }
-
- /// Get a keyword argument with the given key and type if it is present.
- pub fn get_key_opt<T>(&mut self, key: &str) -> ParseResult<Option<Spanned<T::Output>>>
- where T: Argument<'a> {
- self.args.keyword.iter()
- .find(|entry| entry.val.0.val == key)
- .map(|entry| T::from_expr(&entry.val.1))
- .transpose()
- }
-
- /// Assert that there are no positional arguments left. Returns an error
- /// otherwise.
- pub fn done(&self) -> ParseResult<()> {
- if self.positional_index == self.args.positional.len() {
- Ok(())
- } else {
- error!(unexpected_argument);
- }
- }
-
- /// Covert an option to a result with an error on `None`.
- fn expected<T>(val: Option<Spanned<T::Output>>) -> ParseResult<Spanned<T::Output>>
- where T: Argument<'a> {
- val.ok_or_else(|| error!(@"expected {}", T::ERROR_MESSAGE))
- }
-}
-
-/// A kind of argument.
-pub trait Argument<'a> {
- type Output;
- const ERROR_MESSAGE: &'static str;
-
- fn from_expr(expr: &'a Spanned<Expression>) -> ParseResult<Spanned<Self::Output>>;
-}
-
-macro_rules! arg {
- ($type:ident, $err:expr, $doc:expr, $output:ty, $wanted:pat => $converted:expr) => (
- #[doc = $doc]
- #[doc = " argument for use with the [`ArgParser`]."]
- pub struct $type;
- impl<'a> Argument<'a> for $type {
- type Output = $output;
- const ERROR_MESSAGE: &'static str = $err;
-
- fn from_expr(expr: &'a Spanned<Expression>) -> ParseResult<Spanned<Self::Output>> {
- #[allow(unreachable_code)]
- match &expr.val {
- $wanted => Ok(Spanned::new($converted, expr.span)),
- _ => error!("expected {}", $err),
- }
- }
- }
- );
-}
-
-arg!(ArgExpr, "expression", "A generic expression", &'a Expression, expr => &expr);
-arg!(ArgIdent, "identifier", "An identifier (e.g. `horizontal`)", &'a str, Ident(s) => s.as_str());
-arg!(ArgStr, "string", "A string (e.g. `\"Hello\"`)", &'a str, Str(s) => s.as_str());
-arg!(ArgNum, "number", "A number (e.g. `5.4`)", f64, Num(n) => *n);
-arg!(ArgSize, "size", "A size (e.g. `12pt`)", crate::size::Size, Size(s) => *s);
-arg!(ArgBool, "bool", "A boolean (`true` or `false`)", bool, Bool(b) => *b);
-
-/// 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 {
- Primary => GenericAxisKind::Primary,
- Secondary => GenericAxisKind::Secondary,
- Vertical => axes.vertical(),
- 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 {
- Primary => axes.primary(),
- Secondary => axes.secondary(),
- Vertical => SpecificAxisKind::Vertical,
- Horizontal => SpecificAxisKind::Horizontal,
- }
- }
-}
-
-/// 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::*;
- match (self, axis) {
- (Origin, SpecificAxisKind::Horizontal) => Left,
- (End, SpecificAxisKind::Horizontal) => Right,
- (Origin, SpecificAxisKind::Vertical) => Top,
- (End, SpecificAxisKind::Vertical) => Bottom,
- _ => *self,
- }
- }
-}
-
-/// 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),
-}
diff --git a/src/func/macros.rs b/src/func/macros.rs
index 0ffdc857..daae2769 100644
--- a/src/func/macros.rs
+++ b/src/func/macros.rs
@@ -10,6 +10,13 @@ macro_rules! function {
function!(@meta $type | $($rest)*);
};
+ // Parse a tuple struct.
+ ($(#[$outer:meta])* pub struct $type:ident($($fields:tt)*); $($rest:tt)*) => {
+ $(#[$outer])*
+ pub struct $type($($fields)*);
+ function!(@meta $type | $($rest)*);
+ };
+
// Parse a struct with fields.
($(#[$outer:meta])* pub struct $type:ident { $($fields:tt)* } $($rest:tt)*) => {
$(#[$outer])*
@@ -68,14 +75,17 @@ macro_rules! function {
type Meta = $meta;
fn parse(
- header: &FuncHeader,
- $body: Option<&str>,
+ args: FuncArgs,
+ $body: Option<Spanned<&str>>,
$ctx: ParseContext,
$metadata: Self::Meta,
) -> ParseResult<Self> where Self: Sized {
- let mut $args = $crate::func::args::ArgParser::new(&header.args);
+ #[allow(unused_mut)]
+ let mut $args = args;
let val = $code;
- $args.done()?;
+ if !$args.is_empty() {
+ error!(unexpected_argument);
+ }
Ok(val)
}
}
@@ -117,7 +127,7 @@ macro_rules! parse {
(optional: $body:expr, $ctx:expr) => (
if let Some(body) = $body {
- Some($crate::syntax::parse(body, $ctx)?)
+ Some($crate::syntax::parse(body.v, $ctx)?)
} else {
None
}
@@ -125,7 +135,7 @@ macro_rules! parse {
(expected: $body:expr, $ctx:expr) => (
if let Some(body) = $body {
- $crate::syntax::parse(body, $ctx)?
+ $crate::syntax::parse(body.v, $ctx)?
} else {
error!("expected body");
}
diff --git a/src/func/map.rs b/src/func/map.rs
new file mode 100644
index 00000000..880fe3e6
--- /dev/null
+++ b/src/func/map.rs
@@ -0,0 +1,59 @@
+//! 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()
+ }
+}
diff --git a/src/func/mod.rs b/src/func/mod.rs
index 31e31592..7ee7d779 100644
--- a/src/func/mod.rs
+++ b/src/func/mod.rs
@@ -8,17 +8,12 @@ use self::prelude::*;
#[macro_use]
pub mod macros;
-pub mod args;
+pub mod map;
/// Useful imports for creating your own functions.
pub mod prelude {
- pub use Command::*;
- pub use super::args::*;
- pub use super::{Scope, ParseFunc, LayoutFunc, Command, Commands};
- pub use crate::syntax::{SyntaxTree, FuncHeader, FuncArgs, Expression, Spanned, Span};
- pub use crate::syntax::{parse, ParseContext, ParseResult};
- pub use crate::size::{Size, Size2D, SizeBox};
- pub use crate::style::{PageStyle, TextStyle};
+ pub use super::map::ConsistentMap;
+ pub use crate::func::{Scope, ParseFunc, LayoutFunc, Command, Commands};
pub use crate::layout::{
layout_tree, Layout, MultiLayout,
LayoutContext, LayoutSpace, LayoutSpaces,
@@ -26,16 +21,25 @@ pub mod prelude {
LayoutAlignment, Alignment,
SpacingKind, LayoutResult,
};
+ pub use crate::syntax::{
+ parse, ParseContext, ParseResult,
+ SyntaxTree, FuncCall, FuncArgs, PosArg, KeyArg,
+ Expression, Ident, ExpressionKind,
+ Spanned, Span
+ };
+ pub use crate::size::{Size, Size2D, SizeBox, ScaleSize, FSize, PSize};
+ pub use crate::style::{LayoutStyle, PageStyle, TextStyle};
+ pub use Command::*;
}
/// Types representing functions that are parsed from source code.
pub trait ParseFunc {
- type Meta;
+ type Meta: Clone;
/// Parse the header and body into this function given a context.
fn parse(
- header: &FuncHeader,
- body: Option<&str>,
+ args: FuncArgs,
+ body: Option<Spanned<&str>>,
ctx: ParseContext,
metadata: Self::Meta,
) -> ParseResult<Self> where Self: Sized;
@@ -126,8 +130,8 @@ pub struct Scope {
/// A function which parses the source of a function into a function type which
/// implements [`LayoutFunc`].
type Parser = dyn Fn(
- &FuncHeader,
- Option<&str>,
+ FuncArgs,
+ Option<Spanned<&str>>,
ParseContext
) -> ParseResult<Box<dyn LayoutFunc>>;
@@ -153,11 +157,11 @@ impl Scope {
/// Add a parseable type with additional metadata that is given to the
/// parser (other than the default of `()`).
pub fn add_with_metadata<F, T>(&mut self, name: &str, metadata: T)
- where F: ParseFunc<Meta=T> + LayoutFunc + 'static, T: 'static {
+ where F: ParseFunc<Meta=T> + LayoutFunc + 'static, T: 'static + Clone {
self.parsers.insert(
name.to_owned(),
- Box::new(|h, b, c| {
- F::parse(h, b, c, metadata)
+ Box::new(move |a, b, c| {
+ F::parse(a, b, c, metadata.clone())
.map(|f| Box::new(f) as Box<dyn LayoutFunc>)
})
);