diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-08-04 13:48:07 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-08-04 13:48:07 +0200 |
| commit | 2467cd6272c13b618ad53c5dadff5b8c8e7885bf (patch) | |
| tree | 6ad13ec06a04997564efc514b40daa3fb65233e2 /src/syntax | |
| parent | ed4fdcb0ada909f1cc3d7436334e253f0ec14d55 (diff) | |
Refactor function parsing ♻
Diffstat (limited to 'src/syntax')
| -rw-r--r-- | src/syntax/mod.rs | 8 | ||||
| -rw-r--r-- | src/syntax/parsing.rs | 48 | ||||
| -rw-r--r-- | src/syntax/scope.rs | 36 | ||||
| -rw-r--r-- | src/syntax/test.rs | 44 | ||||
| -rw-r--r-- | src/syntax/tree.rs | 12 | ||||
| -rw-r--r-- | src/syntax/value.rs | 10 |
6 files changed, 56 insertions, 102 deletions
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index e1c9bbb0..e0f4e4c8 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -12,11 +12,3 @@ pub mod span; pub mod tokens; pub mod tree; pub mod value; - -/// Basic types used around the syntax side. -pub mod prelude { - pub use super::expr::*; - pub use super::span::{Span, SpanVec, Spanned}; - pub use super::tree::{DynamicNode, SyntaxNode, SyntaxTree}; - pub use super::value::*; -} diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs index 3c802074..bf16e146 100644 --- a/src/syntax/parsing.rs +++ b/src/syntax/parsing.rs @@ -8,36 +8,16 @@ use super::expr::*; use super::scope::Scope; use super::span::{Pos, Span, Spanned}; use super::tokens::{is_newline_char, Token, TokenMode, Tokens}; -use super::tree::{DynamicNode, SyntaxNode, SyntaxTree}; +use super::tree::{SyntaxNode, SyntaxTree}; /// A function which parses a function call into a dynamic node. -pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn DynamicNode>>; - -/// Parse a function call. -pub trait ParseCall { - /// Metadata whose value is passed to `parse`. 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. - /// - /// For example, the functions `h` and `v` are built on the same type. - type Meta: Clone; - - /// Parse the function call. - fn parse( - call: FuncCall, - state: &ParseState, - metadata: Self::Meta, - ) -> Pass<Self> - where - Self: Sized; -} +pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass<SyntaxNode>; /// An invocation of a function. #[derive(Debug, Clone, PartialEq)] pub struct FuncCall<'s> { pub header: FuncHeader, - /// The body as a raw string containing what's inside of the brackets. - pub body: Option<Spanned<&'s str>>, + pub body: FuncBody<'s>, } /// The parsed header of a function (everything in the first set of brackets). @@ -47,6 +27,10 @@ pub struct FuncHeader { pub args: FuncArgs, } +/// The body of a function as a raw spanned string containing what's inside of +/// the brackets. +pub type FuncBody<'s> = Option<Spanned<&'s str>>; + /// The positional and keyword arguments passed to a function. #[derive(Debug, Default, Clone, PartialEq)] pub struct FuncArgs { @@ -215,7 +199,7 @@ impl<'s> FuncParser<'s> { let call = FuncCall { header, body: self.body }; let parsed = parser(call, self.state); self.feedback.extend(parsed.feedback); - Pass::new(SyntaxNode::Dyn(parsed.output), self.feedback) + Pass::new(parsed.output, self.feedback) } fn parse_func_header(&mut self) -> Option<FuncHeader> { @@ -678,7 +662,7 @@ fn unescape_raw(raw: &str) -> Vec<String> { mod tests { use crate::length::Length; use crate::syntax::span::SpanVec; - use crate::syntax::test::{check, DebugFn}; + use crate::syntax::test::{check, debug_func, DebugNode}; use super::*; use Decoration::*; @@ -697,11 +681,11 @@ mod tests { }; ($source:expr => [$($tree:tt)*], [$($diagnostics:tt)*] $(, [$($decos:tt)*])? $(,)?) => { - let mut scope = Scope::new::<DebugFn>(); - scope.add::<DebugFn>("f"); - scope.add::<DebugFn>("n"); - scope.add::<DebugFn>("box"); - scope.add::<DebugFn>("val"); + let mut scope = Scope::new(Box::new(debug_func)); + scope.insert("f", Box::new(debug_func)); + scope.insert("n", Box::new(debug_func)); + scope.insert("box", Box::new(debug_func)); + scope.insert("val", Box::new(debug_func)); let state = ParseState { scope }; let pass = parse($source, Pos::ZERO, &state); @@ -798,13 +782,13 @@ mod tests { value: Z($value), })));)*)? )? - SyntaxNode::Dyn(Box::new(DebugFn { + SyntaxNode::boxed(DebugNode { header: FuncHeader { name: span_item!($name).map(|s| Ident(s.to_string())), args, }, body: func!(@body $($($body)*)?), - })) + }) }}; (@body [$($body:tt)*]) => { Some(span_vec![$($body)*].0) }; (@body) => { None }; diff --git a/src/syntax/scope.rs b/src/syntax/scope.rs index aac2b1b8..c17ff64d 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 super::parsing::{CallParser, ParseCall}; -use super::tree::DynamicNode; +use super::parsing::CallParser; /// A map from identifiers to function parsers. pub struct Scope { @@ -15,31 +14,16 @@ pub struct Scope { impl Scope { /// Create a new empty scope with a fallback parser that is invoked when no /// match is found. - pub fn new<F>() -> Self - where - F: ParseCall<Meta = ()> + DynamicNode + 'static - { + pub fn new(fallback: Box<CallParser>) -> Self { Self { parsers: HashMap::new(), - fallback: make_parser::<F>(()), + fallback, } } /// Associate the given function name with a dynamic node type. - pub fn add<F>(&mut self, name: &str) - where - F: ParseCall<Meta = ()> + DynamicNode + 'static - { - self.add_with_meta::<F>(name, ()); - } - - /// Add a dynamic node type with additional metadata that is passed to the - /// parser. - 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)); + pub fn insert(&mut self, name: impl Into<String>, parser: Box<CallParser>) { + self.parsers.insert(name.into(), parser); } /// Return the parser with the given name if there is one. @@ -58,13 +42,3 @@ impl Debug for Scope { f.debug_set().entries(self.parsers.keys()).finish() } } - -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 9faa7f23..7dec20e3 100644 --- a/src/syntax/test.rs +++ b/src/syntax/test.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use crate::func::parse_maybe_body; +use crate::func::prelude::*; use super::decoration::Decoration; use super::expr::{Expr, Ident, NamedTuple, Object, Pair, Tuple}; use super::parsing::{FuncArg, FuncArgs, FuncHeader}; @@ -58,26 +58,26 @@ macro_rules! span_item { }; } -function! { - /// Most functions in the tests are parsed into the debug function for easy - /// inspection of arguments and body. - #[derive(Debug, Clone, PartialEq)] - pub struct DebugFn { - pub header: FuncHeader, - pub body: Option<SyntaxTree>, - } +pub fn debug_func(call: FuncCall, state: &ParseState) -> Pass<SyntaxNode> { + let mut f = Feedback::new(); + let node = DebugNode { + header: call.header, + body: parse_body_maybe(call.body, state, &mut f), + }; + Pass::node(node, f) +} - parse(header, body, state, f) { - let cloned = header.clone(); - header.args.pos.0.clear(); - header.args.key.0.clear(); - Self { - header: cloned, - body: parse_maybe_body(body, state, f), - } - } +#[derive(Debug, Clone, PartialEq)] +pub struct DebugNode { + pub header: FuncHeader, + pub body: Option<SyntaxTree>, +} - layout(self, ctx, f) { vec![] } +#[async_trait(?Send)] +impl Layout for DebugNode { + async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>> { + unimplemented!() + } } /// Compares elements by only looking at values and ignoring spans. @@ -87,8 +87,8 @@ pub trait SpanlessEq<Rhs = Self> { impl SpanlessEq for SyntaxNode { fn spanless_eq(&self, other: &Self) -> bool { - fn downcast<'a>(func: &'a (dyn DynamicNode + 'static)) -> &'a DebugFn { - func.downcast::<DebugFn>().expect("not a debug fn") + fn downcast<'a>(func: &'a (dyn DynamicNode + 'static)) -> &'a DebugNode { + func.downcast::<DebugNode>().expect("not a debug node") } match (self, other) { @@ -101,7 +101,7 @@ impl SpanlessEq for SyntaxNode { } } -impl SpanlessEq for DebugFn { +impl SpanlessEq for DebugNode { fn spanless_eq(&self, other: &Self) -> bool { self.header.spanless_eq(&other.header) && self.body.spanless_eq(&other.body) diff --git a/src/syntax/tree.rs b/src/syntax/tree.rs index 73734d28..4ce39cd4 100644 --- a/src/syntax/tree.rs +++ b/src/syntax/tree.rs @@ -31,6 +31,13 @@ pub enum SyntaxNode { Dyn(Box<dyn DynamicNode>), } +impl SyntaxNode { + /// Create a `Dyn` variant from an unboxed dynamic node. + pub fn boxed<T: DynamicNode + 'static>(node: T) -> SyntaxNode { + SyntaxNode::Dyn(Box::new(node)) + } +} + impl PartialEq for SyntaxNode { fn eq(&self, other: &SyntaxNode) -> bool { use SyntaxNode::*; @@ -65,10 +72,7 @@ pub trait DynamicNode: Debug + Layout { impl dyn DynamicNode { /// Downcast this dynamic node to a concrete node. - pub fn downcast<T>(&self) -> Option<&T> - where - T: DynamicNode + 'static, - { + pub fn downcast<T: DynamicNode + 'static>(&self) -> Option<&T> { self.as_any().downcast_ref::<T>() } } diff --git a/src/syntax/value.rs b/src/syntax/value.rs index b7211eaf..c523ff93 100644 --- a/src/syntax/value.rs +++ b/src/syntax/value.rs @@ -2,7 +2,7 @@ use fontdock::{FontStyle, FontWeight, FontWidth}; -use crate::layout::prelude::*; +use crate::layout::{Dir, SpecAlign}; use crate::length::{Length, ScaleLength}; use crate::paper::Paper; use crate::Feedback; @@ -103,10 +103,10 @@ macro_rules! ident_value { } ident_value!(Dir, "direction", |s| match s { - "ltr" => Some(LTR), - "rtl" => Some(RTL), - "ttb" => Some(TTB), - "btt" => Some(BTT), + "ltr" => Some(Self::LTR), + "rtl" => Some(Self::RTL), + "ttb" => Some(Self::TTB), + "btt" => Some(Self::BTT), _ => None, }); |
