summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-08-02 22:21:58 +0200
committerLaurenz <laurmaedje@gmail.com>2020-08-02 22:21:58 +0200
commit5a8f2fb73ddafba9fdbe952385ae2676126183ae (patch)
treef65dcc5c8fc3809171e0cc1a985d466159ccb254
parent266d457292e7461d448f9141030028ea68b573d1 (diff)
Replace body! macro with functions 🧰
-rw-r--r--src/func.rs107
-rw-r--r--src/library/font.rs4
-rw-r--r--src/library/layout.rs8
-rw-r--r--src/library/mod.rs4
-rw-r--r--src/library/page.rs2
-rw-r--r--src/library/spacing.rs2
-rw-r--r--src/syntax/mod.rs8
-rw-r--r--src/syntax/parsing.rs21
-rw-r--r--src/syntax/scope.rs15
-rw-r--r--src/syntax/test.rs3
10 files changed, 87 insertions, 87 deletions
diff --git a/src/func.rs b/src/func.rs
index afc796ae..bb2862d2 100644
--- a/src/func.rs
+++ b/src/func.rs
@@ -1,44 +1,20 @@
-//! Trait and prelude for custom functions.
+//! Tools for building custom functions.
-use crate::{Pass, Feedback};
-use crate::syntax::parsing::{FuncCall, ParseState};
-use crate::syntax::span::Span;
+use crate::Feedback;
+use crate::syntax::span::{Span, Spanned};
+use crate::syntax::parsing::{parse, ParseState};
+use crate::syntax::tree::SyntaxTree;
-/// Types that are useful for creating your own functions.
+/// Useful things for creating functions.
pub mod prelude {
- pub use crate::{function, body, error, warning};
pub use crate::layout::prelude::*;
pub use crate::layout::Command::{self, *};
- pub use crate::style::{LayoutStyle, PageStyle, TextStyle};
- pub use crate::syntax::expr::*;
- pub use crate::syntax::tree::SyntaxTree;
- pub use crate::syntax::span::{Span, Spanned};
- pub use crate::syntax::value::*;
- pub use super::OptionExt;
+ pub use crate::syntax::prelude::*;
+ pub use crate::style::*;
+ pub use super::{OptionExt, parse_maybe_body, expect_no_body};
}
-/// Parse a function from source code.
-pub trait ParseFunc {
- /// A metadata type whose value is passed into the function parser. This
- /// allows a single function to do different things depending on the value
- /// that needs to be given when inserting the function into a
- /// [scope](crate::syntax::Scope).
- ///
- /// For example, the functions `word.spacing`, `line.spacing` and
- /// `par.spacing` are actually all the same function
- /// [`ContentSpacingFunc`](crate::library::ContentSpacingFunc) with the
- /// metadata specifiy which content should be spaced.
- type Meta: Clone;
-
- /// Parse the header and body into this function given a context.
- fn parse(
- header: FuncCall,
- state: &ParseState,
- metadata: Self::Meta,
- ) -> Pass<Self> where Self: Sized;
-}
-
-/// Extra methods on [`Options`](Option) used for argument parsing.
+/// Extra methods on [`Options`](Option) used for function argument parsing.
pub trait OptionExt<T>: Sized {
/// Calls `f` with `val` if this is `Some(val)`.
fn with(self, f: impl FnOnce(T));
@@ -63,10 +39,30 @@ impl<T> OptionExt<T> for Option<T> {
}
}
-/// Allows to implement a function type concisely.
+/// Parses a function's body if there is one or returns `None` otherwise.
+pub fn parse_maybe_body(
+ body: Option<Spanned<&str>>,
+ state: &ParseState,
+ f: &mut Feedback,
+) -> Option<SyntaxTree> {
+ body.map(|body| {
+ let parsed = parse(body.v, body.span.start, state);
+ f.extend(parsed.feedback);
+ parsed.output
+ })
+}
+
+/// Generates an error if there is function body even though none was expected.
+pub fn expect_no_body(body: Option<Spanned<&str>>, f: &mut Feedback) {
+ if let Some(body) = body {
+ error!(@f, body.span, "unexpected body");
+ }
+}
+
+/// Implement a custom function concisely.
///
/// # Examples
-/// Look at the source code of the [`library`](crate::library) module for more
+/// Look at the source code of the [`library`](crate::library) module for
/// examples on how the macro works.
#[macro_export]
macro_rules! function {
@@ -101,7 +97,7 @@ macro_rules! function {
$feedback:ident,
$metadata:ident
) $code:block $($r:tt)*) => {
- impl $crate::func::ParseFunc for $name {
+ impl $crate::syntax::parsing::ParseCall for $name {
type Meta = $meta;
fn parse(
@@ -111,7 +107,7 @@ macro_rules! function {
) -> $crate::Pass<Self> where Self: Sized {
let mut feedback = $crate::Feedback::new();
#[allow(unused)] let $header = &mut call.header;
- #[allow(unused)] let $body = &mut call.body;
+ #[allow(unused)] let $body = call.body;
#[allow(unused)] let $feedback = &mut feedback;
let func = $code;
@@ -131,7 +127,11 @@ macro_rules! function {
function!(@layout($name) $($r)*);
};
- (@layout($name:ident) layout($this:ident, $ctx:ident, $feedback:ident) $code:block) => {
+ (@layout($name:ident) layout(
+ $this:ident,
+ $ctx:ident,
+ $feedback:ident
+ ) $code:block) => {
impl $crate::layout::Layout for $name {
fn layout<'a, 'b, 't>(
#[allow(unused)] &'a $this,
@@ -152,32 +152,3 @@ macro_rules! function {
}
};
}
-
-/// Parse the body of a function.
-///
-/// - If the function does not expect a body, use `body!(nope: body, feedback)`.
-/// - If the function can have a body, use `body!(opt: body, state, feedback,
-/// decos)`.
-///
-/// # Arguments
-/// - The `$body` should be of type `Option<Spanned<&str>>`.
-/// - The `$state` is the parse state to use.
-/// - The `$feedback` should be a mutable references to a
-/// [`Feedback`](crate::Feedback) struct which is filled with the feedback
-/// information arising from parsing.
-#[macro_export]
-macro_rules! body {
- (opt: $body:expr, $state:expr, $feedback:expr) => ({
- $body.map(|body| {
- let parsed = $crate::syntax::parsing::parse(body.v, body.span.start, $state);
- $feedback.extend(parsed.feedback);
- parsed.output
- })
- });
-
- (nope: $body:expr, $feedback:expr) => {
- if let Some(body) = $body {
- error!(@$feedback, body.span, "unexpected body");
- }
- };
-}
diff --git a/src/library/font.rs b/src/library/font.rs
index efcbb86f..21ac14c7 100644
--- a/src/library/font.rs
+++ b/src/library/font.rs
@@ -17,7 +17,7 @@ function! {
classes: Vec<(String, Vec<String>)>,
}
- parse(header, body, ctx, f) {
+ parse(header, body, state, f) {
let size = header.args.pos.get::<ScaleLength>();
let style = header.args.key.get::<FontStyle>("style", f);
@@ -41,7 +41,7 @@ function! {
.collect();
FontFunc {
- body: body!(opt: body, ctx, f),
+ body: parse_maybe_body(body, state, f),
size,
style,
weight,
diff --git a/src/library/layout.rs b/src/library/layout.rs
index d46265a4..d6d02436 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -12,9 +12,9 @@ function! {
height: Option<ScaleLength>,
}
- parse(header, body, ctx, f) {
+ parse(header, body, state, f) {
BoxFunc {
- body: body!(opt: body, ctx, f).unwrap_or(SyntaxTree::new()),
+ body: parse_maybe_body(body, state, f).unwrap_or(SyntaxTree::new()),
width: header.args.key.get::<ScaleLength>("width", f),
height: header.args.key.get::<ScaleLength>("height", f),
}
@@ -56,9 +56,9 @@ function! {
v: Option<Spanned<SpecAlign>>,
}
- parse(header, body, ctx, f) {
+ parse(header, body, state, f) {
AlignFunc {
- body: body!(opt: body, ctx, f),
+ body: parse_maybe_body(body, state, f),
aligns: header.args.pos.all::<Spanned<SpecAlign>>().collect(),
h: header.args.key.get::<Spanned<SpecAlign>>("horizontal", f),
v: header.args.key.get::<Spanned<SpecAlign>>("vertical", f),
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 7a664257..7240e42b 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -1,4 +1,4 @@
-//! The _Typst_ standard library.
+//! The standard library.
use crate::func::prelude::*;
use crate::layout::{LayoutContext, Commands};
@@ -37,7 +37,7 @@ function! {
parse(header, body, state, f) {
header.args.pos.0.clear();
header.args.key.0.clear();
- ValFunc { body: body!(opt: body, state, f) }
+ ValFunc { body: parse_maybe_body(body, state, f), }
}
layout(self, ctx, f) {
diff --git a/src/library/page.rs b/src/library/page.rs
index d1964fd2..faf08ee0 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -20,7 +20,7 @@ function! {
}
parse(header, body, state, f) {
- body!(nope: body, f);
+ expect_no_body(body, f);
PageFunc {
paper: header.args.pos.get::<Paper>(),
width: header.args.key.get::<Length>("width", f),
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
index 22c4669e..d8263694 100644
--- a/src/library/spacing.rs
+++ b/src/library/spacing.rs
@@ -34,7 +34,7 @@ function! {
type Meta = SpecAxis;
parse(header, body, state, f, meta) {
- body!(nope: body, f);
+ expect_no_body(body, f);
SpacingFunc {
spacing: header.args.pos.expect::<ScaleLength>(f)
.map(|s| (meta, s))
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 86c2fd24..6b1f3d08 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -4,6 +4,14 @@
#[macro_use]
mod test;
+/// Basic types used around the syntax side.
+pub mod prelude {
+ pub use super::expr::*;
+ pub use super::tree::{SyntaxTree, SyntaxNode, DynamicNode};
+ pub use super::span::{SpanVec, Span, Spanned};
+ pub use super::value::*;
+}
+
pub mod decoration;
pub mod expr;
pub mod tree;
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index 7594c14d..bcbcb8d4 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -13,6 +13,27 @@ use super::tree::{SyntaxTree, SyntaxNode, DynamicNode};
/// A function which parses a function call into a tree.
pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn DynamicNode>>;
+/// Parse a function call.
+pub trait ParseCall {
+ /// A metadata type whose value is passed into the function parser. This
+ /// allows a single function to do different things depending on the value
+ /// that needs to be given when inserting the function into a
+ /// [scope](crate::syntax::Scope).
+ ///
+ /// For example, the functions `word.spacing`, `line.spacing` and
+ /// `par.spacing` are actually all the same function
+ /// [`ContentSpacingFunc`](crate::library::ContentSpacingFunc) with the
+ /// metadata specifiy which content should be spaced.
+ type Meta: Clone;
+
+ /// Parse the header and body into this function given a context.
+ fn parse(
+ header: FuncCall,
+ state: &ParseState,
+ metadata: Self::Meta,
+ ) -> Pass<Self> where Self: Sized;
+}
+
/// An invocation of a function.
#[derive(Debug, Clone, PartialEq)]
pub struct FuncCall<'s> {
diff --git a/src/syntax/scope.rs b/src/syntax/scope.rs
index d3092944..8fdad6a0 100644
--- a/src/syntax/scope.rs
+++ b/src/syntax/scope.rs
@@ -3,8 +3,7 @@
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
-use crate::func::ParseFunc;
-use super::parsing::CallParser;
+use super::parsing::{CallParser, ParseCall};
use super::tree::DynamicNode;
/// A map from identifiers to function parsers.
@@ -17,7 +16,7 @@ impl Scope {
/// Create a new empty scope with a fallback parser that is invoked when no
/// match is found.
pub fn new<F>() -> Scope
- where F: ParseFunc<Meta=()> + DynamicNode + 'static {
+ where F: ParseCall<Meta=()> + DynamicNode + 'static {
Scope {
parsers: HashMap::new(),
fallback: make_parser::<F>(()),
@@ -31,14 +30,14 @@ impl Scope {
/// Associate the given name with a type that is parseable into a function.
pub fn add<F>(&mut self, name: &str)
- where F: ParseFunc<Meta=()> + DynamicNode + 'static {
+ where F: ParseCall<Meta=()> + DynamicNode + 'static {
self.add_with_meta::<F>(name, ());
}
/// Add a parseable type with additional metadata that is given to the
/// parser (other than the default of `()`).
- pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as ParseFunc>::Meta)
- where F: ParseFunc + DynamicNode + 'static {
+ pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as ParseCall>::Meta)
+ where F: ParseCall + DynamicNode + 'static {
self.parsers.insert(
name.to_string(),
make_parser::<F>(metadata),
@@ -64,8 +63,8 @@ impl Debug for Scope {
}
}
-fn make_parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<CallParser>
-where F: ParseFunc + DynamicNode + 'static {
+fn make_parser<F>(metadata: <F as ParseCall>::Meta) -> Box<CallParser>
+where F: ParseCall + DynamicNode + 'static {
Box::new(move |f, s| {
F::parse(f, s, metadata.clone())
.map(|tree| Box::new(tree) as Box<dyn DynamicNode>)
diff --git a/src/syntax/test.rs b/src/syntax/test.rs
index b701e577..504bc334 100644
--- a/src/syntax/test.rs
+++ b/src/syntax/test.rs
@@ -1,5 +1,6 @@
use std::fmt::Debug;
+use crate::func::parse_maybe_body;
use super::decoration::Decoration;
use super::expr::{Expr, Ident, Tuple, NamedTuple, Object, Pair};
use super::parsing::{FuncHeader, FuncArgs, FuncArg};
@@ -71,7 +72,7 @@ function! {
header.args.key.0.clear();
DebugFn {
header: cloned,
- body: body!(opt: body, state, f),
+ body: parse_maybe_body(body, state, f),
}
}