summaryrefslogtreecommitdiff
path: root/src/syntax
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-08-04 13:48:07 +0200
committerLaurenz <laurmaedje@gmail.com>2020-08-04 13:48:07 +0200
commit2467cd6272c13b618ad53c5dadff5b8c8e7885bf (patch)
tree6ad13ec06a04997564efc514b40daa3fb65233e2 /src/syntax
parented4fdcb0ada909f1cc3d7436334e253f0ec14d55 (diff)
Refactor function parsing ♻
Diffstat (limited to 'src/syntax')
-rw-r--r--src/syntax/mod.rs8
-rw-r--r--src/syntax/parsing.rs48
-rw-r--r--src/syntax/scope.rs36
-rw-r--r--src/syntax/test.rs44
-rw-r--r--src/syntax/tree.rs12
-rw-r--r--src/syntax/value.rs10
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,
});