1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
//! Tools for building custom functions.
/// Useful things for creating functions.
pub mod prelude {
pub use crate::layout::prelude::*;
pub use crate::layout::Command::{self, *};
pub use crate::style::*;
pub use crate::syntax::prelude::*;
pub use super::{expect_no_body, parse_maybe_body, OptionExt};
}
use crate::syntax::parsing::{parse, ParseState};
use crate::syntax::span::{Span, Spanned};
use crate::syntax::tree::SyntaxTree;
use crate::Feedback;
/// Extra methods on `Option`s 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));
/// Reports an error about a missing argument with the given name and span
/// if the option is `None`.
fn or_missing(self, span: Span, arg: &str, f: &mut Feedback) -> Self;
}
impl<T> OptionExt<T> for Option<T> {
fn with(self, f: impl FnOnce(T)) {
if let Some(val) = self {
f(val);
}
}
fn or_missing(self, span: Span, arg: &str, f: &mut Feedback) -> Self {
if self.is_none() {
error!(@f, span, "missing argument: {}", arg);
}
self
}
}
/// 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` module for examples on how the
/// macro works.
#[macro_export]
macro_rules! function {
// Entry point.
($(#[$outer:meta])* $v:vis $storage:ident $name:ident $($r:tt)*) => {
function!(@def($name) $(#[$outer])* $v $storage $name $($r)*);
};
(@def($name:ident) $definition:item $($r:tt)*) => {
$definition
function!(@meta($name) $($r)*);
};
// Metadata.
(@meta($name:ident) type Meta = $meta:ty; $($r:tt)*) => {
function!(@parse($name, $meta) $($r)*);
};
(@meta($name:ident) $($r:tt)*) => {
function!(@parse($name, ()) $($r)*);
};
// Parse trait.
(@parse($($a:tt)*) parse(default) $($r:tt)*) => {
function!(@parse($($a)*) parse(_h, _b, _c, _f, _m) { Default::default() } $($r)*);
};
(@parse($($a:tt)*) parse($h:ident, $b:ident, $c:ident, $f:ident) $($r:tt)* ) => {
function!(@parse($($a)*) parse($h, $b, $c, $f, _metadata) $($r)*);
};
(@parse($name:ident, $meta:ty) parse(
$header:ident,
$body:ident,
$state:ident,
$feedback:ident,
$metadata:ident
) $code:block $($r:tt)*) => {
impl $crate::syntax::parsing::ParseCall for $name {
type Meta = $meta;
fn parse(
#[allow(unused)] mut call: $crate::syntax::parsing::FuncCall,
#[allow(unused)] $state: &$crate::syntax::parsing::ParseState,
#[allow(unused)] $metadata: Self::Meta,
) -> $crate::Pass<Self>
where
Self: Sized,
{
let mut feedback = $crate::Feedback::new();
#[allow(unused)] let $header = &mut call.header;
#[allow(unused)] let $body = call.body;
#[allow(unused)] let $feedback = &mut feedback;
let func = $code;
for arg in call.header.args.pos.0 {
error!(@feedback, arg.span, "unexpected argument");
}
for arg in call.header.args.key.0 {
error!(@feedback, arg.span, "unexpected argument");
}
$crate::Pass::new(func, feedback)
}
}
function!(@layout($name) $($r)*);
};
(@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,
#[allow(unused)] mut $ctx: $crate::layout::LayoutContext<'b>,
) -> $crate::DynFuture<'t, $crate::Pass<$crate::layout::Commands<'a>>>
where
'a: 't,
'b: 't,
Self: 't,
{
Box::pin(async move {
let mut feedback = $crate::Feedback::new();
#[allow(unused)] let $feedback = &mut feedback;
let commands = $code;
$crate::Pass::new(commands, feedback)
})
}
}
};
}
|