Desugar body into last argument 🍩

This commit is contained in:
Laurenz 2020-08-14 20:43:03 +02:00
parent 650c712eab
commit c8f6b5bd5c
10 changed files with 56 additions and 79 deletions

View File

@ -8,9 +8,7 @@ pub mod prelude {
pub use crate::layout::Command::{self, *}; pub use crate::layout::Command::{self, *};
pub use crate::style::*; pub use crate::style::*;
pub use crate::syntax::expr::*; pub use crate::syntax::expr::*;
pub use crate::syntax::parsing::{ pub use crate::syntax::parsing::{parse, FuncArgs, FuncCall, ParseState};
parse, FuncArgs, FuncBody, FuncCall, FuncHeader, ParseState,
};
pub use crate::syntax::span::{Span, SpanVec, Spanned}; pub use crate::syntax::span::{Span, SpanVec, Spanned};
pub use crate::syntax::tree::{DynamicNode, SyntaxNode, SyntaxTree}; pub use crate::syntax::tree::{DynamicNode, SyntaxNode, SyntaxTree};
pub use crate::syntax::value::*; pub use crate::syntax::value::*;
@ -55,10 +53,3 @@ pub fn drain_args(args: FuncArgs, f: &mut Feedback) {
error!(@f, arg.span, "unexpected argument"); error!(@f, arg.span, "unexpected argument");
} }
} }
/// Generate an error if there is function body even though none was expected.
pub fn expect_no_body(body: FuncBody, f: &mut Feedback) {
if let Some(body) = body {
error!(@f, body.span, "unexpected body");
}
}

View File

@ -12,9 +12,9 @@ use super::*;
/// There may not be two alignment specifications for the same axis. /// There may not be two alignment specifications for the same axis.
pub fn align(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> { pub fn align(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
let mut f = Feedback::new(); let mut f = Feedback::new();
let mut args = call.header.args; let mut args = call.args;
let node = AlignNode { let node = AlignNode {
body: call.body.map(|s| s.v), content: args.pos.get::<SyntaxTree>(),
aligns: args.pos.all::<Spanned<SpecAlign>>().collect(), aligns: args.pos.all::<Spanned<SpecAlign>>().collect(),
h: args.key.get::<Spanned<SpecAlign>>("horizontal", &mut f), h: args.key.get::<Spanned<SpecAlign>>("horizontal", &mut f),
v: args.key.get::<Spanned<SpecAlign>>("vertical", &mut f), v: args.key.get::<Spanned<SpecAlign>>("vertical", &mut f),
@ -25,7 +25,7 @@ pub fn align(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
struct AlignNode { struct AlignNode {
body: Option<SyntaxTree>, content: Option<SyntaxTree>,
aligns: SpanVec<SpecAlign>, aligns: SpanVec<SpecAlign>,
h: Option<Spanned<SpecAlign>>, h: Option<Spanned<SpecAlign>>,
v: Option<Spanned<SpecAlign>>, v: Option<Spanned<SpecAlign>>,
@ -64,9 +64,9 @@ impl Layout for AlignNode {
} }
} }
Pass::new(match &self.body { Pass::new(match &self.content {
Some(body) => { Some(tree) => {
let layouted = layout(body, ctx).await; let layouted = layout(tree, ctx).await;
f.extend(layouted.feedback); f.extend(layouted.feedback);
vec![AddMultiple(layouted.output)] vec![AddMultiple(layouted.output)]
} }

View File

@ -8,9 +8,9 @@ use super::*;
/// - `height`: The height of the box (length of relative to parent's height). /// - `height`: The height of the box (length of relative to parent's height).
pub fn boxed(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> { pub fn boxed(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
let mut f = Feedback::new(); let mut f = Feedback::new();
let mut args = call.header.args; let mut args = call.args;
let node = BoxNode { let node = BoxNode {
body: call.body.map(|s| s.v).unwrap_or(SyntaxTree::new()), content: args.pos.get::<SyntaxTree>().unwrap_or(SyntaxTree::new()),
width: args.key.get::<ScaleLength>("width", &mut f), width: args.key.get::<ScaleLength>("width", &mut f),
height: args.key.get::<ScaleLength>("height", &mut f), height: args.key.get::<ScaleLength>("height", &mut f),
}; };
@ -20,7 +20,7 @@ pub fn boxed(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
struct BoxNode { struct BoxNode {
body: SyntaxTree, content: SyntaxTree,
width: Option<ScaleLength>, width: Option<ScaleLength>,
height: Option<ScaleLength>, height: Option<ScaleLength>,
} }
@ -45,7 +45,7 @@ impl Layout for BoxNode {
ctx.spaces[0].expansion.vertical = true; ctx.spaces[0].expansion.vertical = true;
}); });
layout(&self.body, ctx).await.map(|out| { layout(&self.content, ctx).await.map(|out| {
let layout = out.into_iter().next().unwrap(); let layout = out.into_iter().next().unwrap();
vec![Add(layout)] vec![Add(layout)]
}) })

View File

@ -20,10 +20,10 @@ use super::*;
/// ``` /// ```
pub fn font(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> { pub fn font(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
let mut f = Feedback::new(); let mut f = Feedback::new();
let mut args = call.header.args; let mut args = call.args;
let node = FontNode { let node = FontNode {
body: call.body.map(|s| s.v), content: args.pos.get::<SyntaxTree>(),
size: args.pos.get::<ScaleLength>(), size: args.pos.get::<ScaleLength>(),
style: args.key.get::<FontStyle>("style", &mut f), style: args.key.get::<FontStyle>("style", &mut f),
weight: args.key.get::<FontWeight>("weight", &mut f), weight: args.key.get::<FontWeight>("weight", &mut f),
@ -53,7 +53,7 @@ pub fn font(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
struct FontNode { struct FontNode {
body: Option<SyntaxTree>, content: Option<SyntaxTree>,
size: Option<ScaleLength>, size: Option<ScaleLength>,
style: Option<FontStyle>, style: Option<FontStyle>,
weight: Option<FontWeight>, weight: Option<FontWeight>,
@ -91,7 +91,7 @@ impl Layout for FontNode {
text.fallback.flatten(); text.fallback.flatten();
Pass::okay(match &self.body { Pass::okay(match &self.content {
Some(tree) => vec![ Some(tree) => vec![
SetTextStyle(text), SetTextStyle(text),
LayoutSyntaxTree(tree), LayoutSyntaxTree(tree),

View File

@ -18,8 +18,7 @@ use super::*;
/// - `flip`: Flips custom or paper-defined width and height (boolean). /// - `flip`: Flips custom or paper-defined width and height (boolean).
pub fn page(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> { pub fn page(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
let mut f = Feedback::new(); let mut f = Feedback::new();
let mut args = call.header.args; let mut args = call.args;
expect_no_body(call.body, &mut f);
let node = PageNode { let node = PageNode {
paper: args.pos.get::<Paper>(), paper: args.pos.get::<Paper>(),
width: args.key.get::<Length>("width", &mut f), width: args.key.get::<Length>("width", &mut f),
@ -79,7 +78,7 @@ impl Layout for PageNode {
/// `pagebreak`: Ends the current page. /// `pagebreak`: Ends the current page.
pub fn pagebreak(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> { pub fn pagebreak(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
let mut f = Feedback::new(); let mut f = Feedback::new();
drain_args(call.header.args, &mut f); drain_args(call.args, &mut f);
Pass::node(PageBreakNode, f) Pass::node(PageBreakNode, f)
} }

View File

@ -20,12 +20,11 @@ pub fn v(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
fn spacing(call: FuncCall, axis: SpecAxis) -> Pass<SyntaxNode> { fn spacing(call: FuncCall, axis: SpecAxis) -> Pass<SyntaxNode> {
let mut f = Feedback::new(); let mut f = Feedback::new();
let mut args = call.header.args; let mut args = call.args;
expect_no_body(call.body, &mut f);
let node = SpacingNode { let node = SpacingNode {
spacing: args.pos.expect::<ScaleLength>(&mut f) spacing: args.pos.expect::<ScaleLength>(&mut f)
.map(|s| (axis, s)) .map(|s| (axis, s))
.or_missing(call.header.name.span, "spacing", &mut f), .or_missing(call.name.span, "spacing", &mut f),
}; };
drain_args(args, &mut f); drain_args(args, &mut f);
Pass::node(node, f) Pass::node(node, f)

View File

@ -5,21 +5,22 @@ use super::*;
/// This is also the fallback function, which is used when a function name /// This is also the fallback function, which is used when a function name
/// cannot be resolved. /// cannot be resolved.
pub fn val(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> { pub fn val(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
let mut args = call.args;
let node = ValNode { let node = ValNode {
body: call.body.map(|s| s.v), content: args.pos.get::<SyntaxTree>(),
}; };
Pass::node(node, Feedback::new()) Pass::node(node, Feedback::new())
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
struct ValNode { struct ValNode {
body: Option<SyntaxTree>, content: Option<SyntaxTree>,
} }
#[async_trait(?Send)] #[async_trait(?Send)]
impl Layout for ValNode { impl Layout for ValNode {
async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>> { async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>> {
Pass::okay(match &self.body { Pass::okay(match &self.content {
Some(tree) => vec![LayoutSyntaxTree(tree)], Some(tree) => vec![LayoutSyntaxTree(tree)],
None => vec![], None => vec![],
}) })

View File

@ -16,21 +16,10 @@ pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass<SyntaxNode>;
/// An invocation of a function. /// An invocation of a function.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct FuncCall { pub struct FuncCall {
pub header: FuncHeader,
pub body: FuncBody,
}
/// The parsed header of a function (everything in the first set of brackets).
#[derive(Debug, Clone, PartialEq)]
pub struct FuncHeader {
pub name: Spanned<Ident>, pub name: Spanned<Ident>,
pub args: FuncArgs, pub args: FuncArgs,
} }
/// The body of a function as a raw spanned string containing what's inside of
/// the brackets.
pub type FuncBody = Option<Spanned<SyntaxTree>>;
/// The positional and keyword arguments passed to a function. /// The positional and keyword arguments passed to a function.
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
pub struct FuncArgs { pub struct FuncArgs {
@ -166,8 +155,8 @@ impl<'s> FuncParser<'s> {
} }
fn parse(mut self) -> Pass<SyntaxNode> { fn parse(mut self) -> Pass<SyntaxNode> {
let (parser, header) = if let Some(header) = self.parse_func_header() { let (parser, mut call) = if let Some(call) = self.parse_func_call() {
let name = header.name.v.as_str(); let name = call.name.v.as_str();
let (parser, deco) = match self.state.scope.get_parser(name) { let (parser, deco) = match self.state.scope.get_parser(name) {
// The function exists in the scope. // The function exists in the scope.
Some(parser) => (parser, Decoration::ResolvedFunc), Some(parser) => (parser, Decoration::ResolvedFunc),
@ -177,32 +166,34 @@ impl<'s> FuncParser<'s> {
// the content of the function is not totally dropped (on a best // the content of the function is not totally dropped (on a best
// effort basis). // effort basis).
None => { None => {
error!(@self.feedback, header.name.span, "unknown function"); error!(@self.feedback, call.name.span, "unknown function");
let parser = self.state.scope.get_fallback_parser(); let parser = self.state.scope.get_fallback_parser();
(parser, Decoration::UnresolvedFunc) (parser, Decoration::UnresolvedFunc)
} }
}; };
self.feedback.decorations.push(Spanned::new(deco, header.name.span)); self.feedback.decorations.push(Spanned::new(deco, call.name.span));
(parser, header) (parser, call)
} else { } else {
// Parse the body with the fallback parser even when the header is // Parse the call with the fallback parser even when the header is
// completely unparsable. // completely unparsable.
let parser = self.state.scope.get_fallback_parser(); let parser = self.state.scope.get_fallback_parser();
let header = FuncHeader { let call = FuncCall {
name: Spanned::new(Ident(String::new()), Span::ZERO), name: Spanned::new(Ident(String::new()), Span::ZERO),
args: FuncArgs::new(), args: FuncArgs::new(),
}; };
(parser, header) (parser, call)
}; };
let body = self.body.map(|body| body.map(|src| { if let Some(body) = self.body {
let parsed = parse(src, body.span.start, &self.state); let tree = body.map(|src| {
self.feedback.extend(parsed.feedback); let parsed = parse(src, body.span.start, &self.state);
parsed.output self.feedback.extend(parsed.feedback);
})); Expr::Tree(parsed.output)
});
let call = FuncCall { header, body }; call.args.pos.push(tree);
}
let parsed = parser(call, self.state); let parsed = parser(call, self.state);
self.feedback.extend(parsed.feedback); self.feedback.extend(parsed.feedback);
@ -210,7 +201,7 @@ impl<'s> FuncParser<'s> {
Pass::new(parsed.output, self.feedback) Pass::new(parsed.output, self.feedback)
} }
fn parse_func_header(&mut self) -> Option<FuncHeader> { fn parse_func_call(&mut self) -> Option<FuncCall> {
let after_bracket = self.pos(); let after_bracket = self.pos();
self.skip_white(); self.skip_white();
@ -229,7 +220,7 @@ impl<'s> FuncParser<'s> {
None => FuncArgs::new(), None => FuncArgs::new(),
}; };
Some(FuncHeader { name, args }) Some(FuncCall { name, args })
} }
fn parse_func_args(&mut self) -> FuncArgs { fn parse_func_args(&mut self) -> FuncArgs {
@ -790,13 +781,13 @@ mod tests {
value: Z($value), value: Z($value),
})));)*)? })));)*)?
)? )?
SyntaxNode::boxed(DebugNode { SyntaxNode::boxed(DebugNode(
header: FuncHeader { FuncCall {
name: span_item!($name).map(|s| Ident(s.to_string())), name: span_item!($name).map(|s| Ident(s.to_string())),
args, args,
}, },
body: func!(@body $($($body)*)?), func!(@body $($($body)*)?),
}) ))
}}; }};
(@body [$($body:tt)*]) => { Some(span_vec![$($body)*].0) }; (@body [$($body:tt)*]) => { Some(span_vec![$($body)*].0) };
(@body) => { None }; (@body) => { None };

View File

@ -3,10 +3,10 @@ use std::fmt::Debug;
use crate::func::prelude::*; use crate::func::prelude::*;
use super::decoration::Decoration; use super::decoration::Decoration;
use super::expr::{Expr, Ident, NamedTuple, Object, Pair, Tuple}; use super::expr::{Expr, Ident, NamedTuple, Object, Pair, Tuple};
use super::parsing::{FuncArg, FuncArgs, FuncHeader}; use super::parsing::{FuncArg, FuncArgs};
use super::span::Spanned; use super::span::Spanned;
use super::tokens::Token; use super::tokens::Token;
use super::tree::{DynamicNode, SyntaxNode, SyntaxTree}; use super::tree::{DynamicNode, SyntaxNode};
pub fn check<T>(src: &str, exp: T, found: T, cmp_spans: bool) pub fn check<T>(src: &str, exp: T, found: T, cmp_spans: bool)
where where
@ -58,19 +58,13 @@ macro_rules! span_item {
}; };
} }
pub fn debug_func(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> { pub fn debug_func(mut call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
let node = DebugNode { let tree = call.args.pos.get::<SyntaxTree>();
header: call.header, Pass::node(DebugNode(call, tree), Feedback::new())
body: call.body.map(|s| s.v),
};
Pass::node(node, Feedback::new())
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct DebugNode { pub struct DebugNode(pub FuncCall, pub Option<SyntaxTree>);
pub header: FuncHeader,
pub body: Option<SyntaxTree>,
}
#[async_trait(?Send)] #[async_trait(?Send)]
impl Layout for DebugNode { impl Layout for DebugNode {
@ -102,12 +96,12 @@ impl SpanlessEq for SyntaxNode {
impl SpanlessEq for DebugNode { impl SpanlessEq for DebugNode {
fn spanless_eq(&self, other: &Self) -> bool { fn spanless_eq(&self, other: &Self) -> bool {
self.header.spanless_eq(&other.header) self.0.spanless_eq(&other.0)
&& self.body.spanless_eq(&other.body) && self.1.spanless_eq(&other.1)
} }
} }
impl SpanlessEq for FuncHeader { impl SpanlessEq for FuncCall {
fn spanless_eq(&self, other: &Self) -> bool { fn spanless_eq(&self, other: &Self) -> bool {
self.name.spanless_eq(&other.name) self.name.spanless_eq(&other.name)
&& self.args.spanless_eq(&other.args) && self.args.spanless_eq(&other.args)

View File

@ -8,6 +8,7 @@ use crate::paper::Paper;
use crate::Feedback; use crate::Feedback;
use super::expr::*; use super::expr::*;
use super::span::Spanned; use super::span::Spanned;
use super::tree::SyntaxTree;
/// Value types are used to extract values from functions, tuples and /// Value types are used to extract values from functions, tuples and
/// objects. They represent the value part of an argument. /// objects. They represent the value part of an argument.
@ -58,6 +59,7 @@ match_value!(String, "string", Expr::Str(s) => s);
match_value!(bool, "bool", Expr::Bool(b) => b); match_value!(bool, "bool", Expr::Bool(b) => b);
match_value!(f64, "number", Expr::Number(n) => n); match_value!(f64, "number", Expr::Number(n) => n);
match_value!(Length, "length", Expr::Length(l) => l); match_value!(Length, "length", Expr::Length(l) => l);
match_value!(SyntaxTree, "tree", Expr::Tree(t) => t);
match_value!(Tuple, "tuple", Expr::Tuple(t) => t); match_value!(Tuple, "tuple", Expr::Tuple(t) => t);
match_value!(Object, "object", Expr::Object(o) => o); match_value!(Object, "object", Expr::Object(o) => o);
match_value!(ScaleLength, "number or length", match_value!(ScaleLength, "number or length",