diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-08-03 16:01:23 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-08-03 16:04:55 +0200 |
| commit | dbfb3d2ced91e56314dfabbb4df9a338926c0a7a (patch) | |
| tree | 678264cb18f8abc81ebe28077f5aef2df4e5a4bd /src/syntax | |
| parent | 5a8f2fb73ddafba9fdbe952385ae2676126183ae (diff) | |
Formatting, documentation and small improvements 🧽
Diffstat (limited to 'src/syntax')
| -rw-r--r-- | src/syntax/decoration.rs | 32 | ||||
| -rw-r--r-- | src/syntax/expr.rs | 156 | ||||
| -rw-r--r-- | src/syntax/mod.rs | 20 | ||||
| -rw-r--r-- | src/syntax/parsing.rs | 116 | ||||
| -rw-r--r-- | src/syntax/scope.rs | 42 | ||||
| -rw-r--r-- | src/syntax/span.rs | 60 | ||||
| -rw-r--r-- | src/syntax/test.rs | 30 | ||||
| -rw-r--r-- | src/syntax/tokens.rs | 37 | ||||
| -rw-r--r-- | src/syntax/tree.rs | 18 | ||||
| -rw-r--r-- | src/syntax/value.rs | 30 |
10 files changed, 248 insertions, 293 deletions
diff --git a/src/syntax/decoration.rs b/src/syntax/decoration.rs index 13a9ad36..a9097444 100644 --- a/src/syntax/decoration.rs +++ b/src/syntax/decoration.rs @@ -13,32 +13,16 @@ pub type Decorations = SpanVec<Decoration>; #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))] pub enum Decoration { - /// A valid function name. - /// ```typst - /// [box] - /// ^^^ - /// ``` - ValidFuncName, - /// An invalid function name. - /// ```typst - /// [blabla] - /// ^^^^^^ - /// ``` - InvalidFuncName, - /// A key of a keyword argument. - /// ```typst - /// [box: width=5cm] - /// ^^^^^ - /// ``` + /// A valid, successfully resolved function name. + ResolvedFunc, + /// An invalid, unresolved function name. + UnresolvedFunc, + /// A key part of a keyword argument. ArgumentKey, - /// A key in an object. - /// ```typst - /// [box: padding={ left: 1cm, right: 2cm}] - /// ^^^^ ^^^^^ - /// ``` + /// A key part of a pair in an object. ObjectKey, - /// An italic word. + /// Text in italics. Italic, - /// A bold word. + /// Text in bold. Bold, } diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 0a9ab149..a637f115 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -5,26 +5,26 @@ use std::ops::Deref; use std::str::FromStr; use std::u8; -use crate::Feedback; use crate::length::Length; -use super::span::{Spanned, SpanVec}; +use crate::Feedback; +use super::span::{SpanVec, Spanned}; use super::tokens::is_identifier; use super::value::Value; -/// An argument or return value. +/// An expression. #[derive(Clone, PartialEq)] pub enum Expr { /// An identifier: `ident`. Ident(Ident), /// A string: `"string"`. Str(String), + /// A boolean: `true, false`. + Bool(bool), /// A number: `1.2, 200%`. Number(f64), /// A length: `2cm, 5.2in`. Length(Length), - /// A bool: `true, false`. - Bool(bool), - /// A color value, including the alpha channel: `#f79143ff`. + /// A color value with alpha channel: `#f79143ff`. Color(RgbaColor), /// A tuple: `(false, 12cm, "hi")`. Tuple(Tuple), @@ -32,37 +32,38 @@ pub enum Expr { NamedTuple(NamedTuple), /// An object: `{ fit: false, width: 12pt }`. Object(Object), - /// An operator that negates the contained expression. + /// An operation that negates the contained expression. Neg(Box<Spanned<Expr>>), - /// An operator that adds the contained expressions. + /// An operation that adds the contained expressions. Add(Box<Spanned<Expr>>, Box<Spanned<Expr>>), - /// An operator that subtracts contained expressions. + /// An operation that subtracts the contained expressions. Sub(Box<Spanned<Expr>>, Box<Spanned<Expr>>), - /// An operator that multiplies the contained expressions. + /// An operation that multiplies the contained expressions. Mul(Box<Spanned<Expr>>, Box<Spanned<Expr>>), - /// An operator that divides the contained expressions. + /// An operation that divides the contained expressions. Div(Box<Spanned<Expr>>, Box<Spanned<Expr>>), } impl Expr { - /// A natural-language name of the type of this expression, e.g. "identifier". + /// A natural-language name of the type of this expression, e.g. + /// "identifier". pub fn name(&self) -> &'static str { use Expr::*; match self { - Ident(_) => "identifier", - Str(_) => "string", - Number(_) => "number", - Length(_) => "length", - Bool(_) => "bool", - Color(_) => "color", - Tuple(_) => "tuple", + Ident(_) => "identifier", + Str(_) => "string", + Bool(_) => "bool", + Number(_) => "number", + Length(_) => "length", + Color(_) => "color", + Tuple(_) => "tuple", NamedTuple(_) => "named tuple", - Object(_) => "object", - Neg(_) => "negation", - Add(_, _) => "addition", - Sub(_, _) => "subtraction", - Mul(_, _) => "multiplication", - Div(_, _) => "division", + Object(_) => "object", + Neg(_) => "negation", + Add(_, _) => "addition", + Sub(_, _) => "subtraction", + Mul(_, _) => "multiplication", + Div(_, _) => "division", } } } @@ -73,9 +74,9 @@ impl Debug for Expr { match self { Ident(i) => i.fmt(f), Str(s) => s.fmt(f), + Bool(b) => b.fmt(f), Number(n) => n.fmt(f), Length(s) => s.fmt(f), - Bool(b) => b.fmt(f), Color(c) => c.fmt(f), Tuple(t) => t.fmt(f), NamedTuple(t) => t.fmt(f), @@ -89,22 +90,15 @@ impl Debug for Expr { } } -/// A unicode identifier. -/// -/// # Example -/// ```typst -/// [func: "hi", ident] -/// ^^^^ ^^^^^ -/// ``` +/// An identifier as defined by unicode with a few extra permissible characters. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Ident(pub String); impl Ident { - /// Create a new identifier from a string checking that it is a valid - /// unicode identifier. - pub fn new<S>(ident: S) -> Option<Ident> where S: AsRef<str> + Into<String> { + /// Create a new identifier from a string checking that it is a valid. + pub fn new(ident: impl AsRef<str> + Into<String>) -> Option<Self> { if is_identifier(ident.as_ref()) { - Some(Ident(ident.into())) + Some(Self(ident.into())) } else { None } @@ -139,36 +133,36 @@ pub struct RgbaColor { pub b: u8, /// Alpha channel. pub a: u8, - /// Indicates whether this is a user-provided value or a - /// default value provided as a fail-over by the parser. - /// This color may be overwritten if this property is true. + /// This is true if this value was provided as a fail-over by the parser + /// because the user-defined value was invalid. This color may be + /// overwritten if this property is true. pub healed: bool, } impl RgbaColor { /// Constructs a new color. - pub fn new(r: u8, g: u8, b: u8, a: u8) -> RgbaColor { - RgbaColor { r, g, b, a, healed: false } + pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self { + Self { r, g, b, a, healed: false } } /// Constructs a new color with the healed property set to true. - pub fn new_healed(r: u8, g: u8, b: u8, a: u8) -> RgbaColor { - RgbaColor { r, g, b, a, healed: true } + pub fn new_healed(r: u8, g: u8, b: u8, a: u8) -> Self { + Self { r, g, b, a, healed: true } } } impl FromStr for RgbaColor { type Err = ParseColorError; - /// Constructs a new color from a hex string like `7a03c2`. - /// Do not specify a leading `#`. - fn from_str(hex_str: &str) -> Result<RgbaColor, Self::Err> { + /// Constructs a new color from a hex string like `7a03c2`. Do not specify a + /// leading `#`. + fn from_str(hex_str: &str) -> Result<Self, Self::Err> { if !hex_str.is_ascii() { return Err(ParseColorError); } let len = hex_str.len(); - let long = len == 6 || len == 8; + let long = len == 6 || len == 8; let short = len == 3 || len == 4; let alpha = len == 4 || len == 8; @@ -192,7 +186,7 @@ impl FromStr for RgbaColor { } } - Ok(RgbaColor::new(values[0], values[1], values[2], values[3])) + Ok(Self::new(values[0], values[1], values[2], values[3])) } } @@ -200,14 +194,12 @@ impl Debug for RgbaColor { fn fmt(&self, f: &mut Formatter) -> fmt::Result { if f.alternate() { write!( - f, - "rgba({:02}, {:02}, {:02}, {:02})", + f, "rgba({:02}, {:02}, {:02}, {:02})", self.r, self.g, self.b, self.a, )?; } else { write!( - f, - "#{:02x}{:02x}{:02x}{:02x}", + f, "#{:02x}{:02x}{:02x}{:02x}", self.r, self.g, self.b, self.a, )?; } @@ -218,7 +210,7 @@ impl Debug for RgbaColor { } } -/// The error returned when parsing a [`RgbaColor`] from a string fails. +/// The error when parsing an `RgbaColor` fails. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct ParseColorError; @@ -226,7 +218,7 @@ impl std::error::Error for ParseColorError {} impl fmt::Display for ParseColorError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.write_str("invalid color") + f.pad("invalid color") } } @@ -241,8 +233,8 @@ pub struct Tuple(pub SpanVec<Expr>); impl Tuple { /// Create an empty tuple. - pub fn new() -> Tuple { - Tuple(vec![]) + pub fn new() -> Self { + Self(vec![]) } /// Add an element. @@ -278,13 +270,13 @@ impl Tuple { let mut i = 0; std::iter::from_fn(move || { while i < self.0.len() { - let val = V::parse(self.0[i].clone(), &mut Feedback::new()); - if val.is_some() { - self.0.remove(i); - return val; - } else { - i += 1; - } + let val = V::parse(self.0[i].clone(), &mut Feedback::new()); + if val.is_some() { + self.0.remove(i); + return val; + } else { + i += 1; + } } None }) @@ -305,16 +297,16 @@ impl Debug for Tuple { /// ``` #[derive(Debug, Clone, PartialEq)] pub struct NamedTuple { - /// The name of the tuple and where it is in the user source. + /// The name of the tuple. pub name: Spanned<Ident>, /// The elements of the tuple. pub tuple: Spanned<Tuple>, } impl NamedTuple { - /// Create a named tuple from a tuple. - pub fn new(name: Spanned<Ident>, tuple: Spanned<Tuple>) -> NamedTuple { - NamedTuple { name, tuple } + /// Create a named tuple from a name and a tuple. + pub fn new(name: Spanned<Ident>, tuple: Spanned<Tuple>) -> Self { + Self { name, tuple } } } @@ -338,24 +330,14 @@ pub struct Object(pub SpanVec<Pair>); /// A key-value pair in an object. #[derive(Debug, Clone, PartialEq)] pub struct Pair { - /// The key part. - /// ```typst - /// key: value - /// ^^^ - /// ``` pub key: Spanned<Ident>, - /// The value part. - /// ```typst - /// key: value - /// ^^^^^ - /// ``` pub value: Spanned<Expr>, } impl Object { /// Create an empty object. - pub fn new() -> Object { - Object(vec![]) + pub fn new() -> Self { + Self(vec![]) } /// Add a pair to object. @@ -384,13 +366,13 @@ impl Object { let mut i = 0; std::iter::from_fn(move || { while i < self.0.len() { - let val = V::parse(self.0[i].v.value.clone(), &mut Feedback::new()); - if let Some(val) = val { - let pair = self.0.remove(i); - return Some((pair.v.key, val)); - } else { - i += 1; - } + let val = V::parse(self.0[i].v.value.clone(), &mut Feedback::new()); + if let Some(val) = val { + let pair = self.0.remove(i); + return Some((pair.v.key, val)); + } else { + i += 1; + } } None }) diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 6b1f3d08..e1c9bbb0 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -4,19 +4,19 @@ #[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; pub mod parsing; -pub mod span; pub mod scope; +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 bcbcb8d4..5141e455 100644 --- a/src/syntax/parsing.rs +++ b/src/syntax/parsing.rs @@ -2,36 +2,34 @@ use std::str::FromStr; -use crate::{Pass, Feedback}; +use crate::{Feedback, Pass}; use super::decoration::Decoration; use super::expr::*; use super::scope::Scope; use super::span::{Pos, Span, Spanned}; -use super::tokens::{is_newline_char, Token, Tokens, TokenMode}; -use super::tree::{SyntaxTree, SyntaxNode, DynamicNode}; +use super::tokens::{is_newline_char, Token, TokenMode, Tokens}; +use super::tree::{DynamicNode, SyntaxNode, SyntaxTree}; -/// A function which parses a function call into a tree. +/// 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 { - /// 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). + /// 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 `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. + /// For example, the functions `h` and `v` are built on the same type. type Meta: Clone; - /// Parse the header and body into this function given a context. + /// Parse the function call. fn parse( - header: FuncCall, + call: FuncCall, state: &ParseState, metadata: Self::Meta, - ) -> Pass<Self> where Self: Sized; + ) -> Pass<Self> + where + Self: Sized; } /// An invocation of a function. @@ -58,8 +56,8 @@ pub struct FuncArgs { impl FuncArgs { /// Create new empty function arguments. - pub fn new() -> FuncArgs { - FuncArgs { + pub fn new() -> Self { + Self { pos: Tuple::new(), key: Object::new(), } @@ -77,9 +75,7 @@ impl FuncArgs { /// Either a positional or keyword argument. #[derive(Debug, Clone, PartialEq)] pub enum FuncArg { - /// A positional argument. Pos(Expr), - /// A keyword argument. Key(Pair), } @@ -116,26 +112,21 @@ pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxTree> { Token::Function { header, body, terminated } => { let parsed = FuncParser::new(header, body, state).parse(); feedback.extend_offset(parsed.feedback, span.start); - if !terminated { error!(@feedback, Span::at(span.end), "expected closing bracket"); } - parsed.output } Token::Star => SyntaxNode::ToggleBolder, Token::Underscore => SyntaxNode::ToggleItalic, Token::Backslash => SyntaxNode::Linebreak, - Token::Raw { raw, terminated } => { if !terminated { error!(@feedback, Span::at(span.end), "expected backtick"); } - SyntaxNode::Raw(unescape_raw(raw)) } - Token::Text(text) => SyntaxNode::Text(text.to_string()), Token::LineComment(_) | Token::BlockComment(_) => continue, @@ -153,17 +144,9 @@ pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxTree> { struct FuncParser<'s> { state: &'s ParseState, - /// ```typst - /// [tokens][body] - /// ^^^^^^ - /// ``` + /// The tokens inside the header. tokens: Tokens<'s>, peeked: Option<Option<Spanned<Token<'s>>>>, - /// The spanned body string if there is a body. - /// ```typst - /// [tokens][body] - /// ^^^^ - /// ``` body: Option<Spanned<&'s str>>, feedback: Feedback, } @@ -173,8 +156,8 @@ impl<'s> FuncParser<'s> { header: &'s str, body: Option<Spanned<&'s str>>, state: &'s ParseState, - ) -> FuncParser<'s> { - FuncParser { + ) -> Self { + Self { state, // Start at column 1 because the opening bracket is also part of // the function, but not part of the `header` string. @@ -190,7 +173,7 @@ impl<'s> FuncParser<'s> { let name = header.name.v.as_str(); let (parser, deco) = match self.state.scope.get_parser(name) { // The function exists in the scope. - Some(parser) => (parser, Decoration::ValidFuncName), + Some(parser) => (parser, Decoration::ResolvedFunc), // The function does not exist in the scope. The parser that is // returned here is a fallback parser which exists to make sure @@ -199,7 +182,7 @@ impl<'s> FuncParser<'s> { None => { error!(@self.feedback, header.name.span, "unknown function"); let parser = self.state.scope.get_fallback_parser(); - (parser, Decoration::InvalidFuncName) + (parser, Decoration::UnresolvedFunc) } }; @@ -261,9 +244,8 @@ impl<'s> FuncParser<'s> { self.skip_white(); let key = ident; - self.feedback.decorations.push( - Spanned::new(Decoration::ArgumentKey, key.span) - ); + self.feedback.decorations + .push(Spanned::new(Decoration::ArgumentKey, key.span)); let value = try_opt_or!(self.parse_expr(), { self.expected("value"); @@ -399,9 +381,9 @@ impl FuncParser<'_> { self.eat_span(Expr::Str(unescape_string(string))) } + Token::ExprBool(b) => self.eat_span(Expr::Bool(b)), Token::ExprNumber(n) => self.eat_span(Expr::Number(n)), Token::ExprLength(s) => self.eat_span(Expr::Length(s)), - Token::ExprBool(b) => self.eat_span(Expr::Bool(b)), Token::ExprHex(s) => { if let Ok(color) = RgbaColor::from_str(s) { self.eat_span(Expr::Color(color)) @@ -411,7 +393,7 @@ impl FuncParser<'_> { let healed = RgbaColor::new_healed(0, 0, 0, 255); self.eat_span(Expr::Color(healed)) } - }, + } // This could be a tuple or a parenthesized expression. We parse as // a tuple in any case and coerce the tuple into a value if it is @@ -500,9 +482,8 @@ impl FuncParser<'_> { continue; } - self.feedback.decorations.push( - Spanned::new(Decoration::ObjectKey, key.span) - ); + self.feedback.decorations + .push(Spanned::new(Decoration::ObjectKey, key.span)); self.skip_white(); let value = try_opt_or!(self.parse_expr(), { @@ -622,7 +603,8 @@ impl<'s> FuncParser<'s> { } fn pos(&self) -> Pos { - self.peeked.flatten() + self.peeked + .flatten() .map(|s| s.span.start) .unwrap_or_else(|| self.tokens.pos()) } @@ -687,10 +669,10 @@ mod tests { use super::*; use Decoration::*; - use Expr::{Number as Num, Length as Len, Bool}; + use Expr::{Bool, Length as Len, Number as Num}; use SyntaxNode::{ - Space as S, ToggleItalic as Italic, ToggleBolder as Bold, - Parbreak, Linebreak, + Space as S, Parbreak, Linebreak, ToggleItalic as Italic, + ToggleBolder as Bold, }; /// Test whether the given string parses into @@ -882,7 +864,7 @@ mod tests { p!("🌎\n*/[n]" => [(0:0, 0:1, T("🌎")), (0:1, 1:0, S), (1:2, 1:5, func!((0:1, 0:2, "n")))], [(1:0, 1:2, "unexpected end of block comment")], - [(1:3, 1:4, ValidFuncName)], + [(1:3, 1:4, ResolvedFunc)], ); } @@ -905,12 +887,12 @@ mod tests { p!("[hi]" => [func!("hi")], [(0:1, 0:3, "unknown function")], - [(0:1, 0:3, InvalidFuncName)], + [(0:1, 0:3, UnresolvedFunc)], ); // A valid name. - p!("[f]" => [func!("f")], [], [(0:1, 0:2, ValidFuncName)]); - p!("[ f]" => [func!("f")], [], [(0:3, 0:4, ValidFuncName)]); + p!("[f]" => [func!("f")], [], [(0:1, 0:2, ResolvedFunc)]); + p!("[ f]" => [func!("f")], [], [(0:3, 0:4, ResolvedFunc)]); // An invalid token for a name. p!("[12]" => [func!("")], [(0:1, 0:3, "expected function name, found number")], []); @@ -923,7 +905,7 @@ mod tests { // Valid. p!("[val: true]" => [func!["val": (Bool(true))]], [], - [(0:1, 0:4, ValidFuncName)], + [(0:1, 0:4, ResolvedFunc)], ); // No colon before arg. @@ -936,7 +918,7 @@ mod tests { p!("[val/🌎:$]" => [func!("val")], [(0:4, 0:4, "expected colon")], - [(0:1, 0:4, ValidFuncName)], + [(0:1, 0:4, ResolvedFunc)], ); // String in invalid header without colon still parsed as string @@ -1159,20 +1141,20 @@ mod tests { // Correct p!("[val: x=true]" => [func!("val": (), { "x" => Bool(true) })], [], - [(0:6, 0:7, ArgumentKey), (0:1, 0:4, ValidFuncName)], + [(0:6, 0:7, ArgumentKey), (0:1, 0:4, ResolvedFunc)], ); // Spacing around keyword arguments p!("\n [val: \n hi \n = /* //\n */ \"s\n\"]" => [S, func!("val": (), { "hi" => Str("s\n") })], [], - [(2:1, 2:3, ArgumentKey), (1:2, 1:5, ValidFuncName)], + [(2:1, 2:3, ArgumentKey), (1:2, 1:5, ResolvedFunc)], ); // Missing value p!("[val: x=]" => [func!("val")], [(0:8, 0:8, "expected value")], - [(0:6, 0:7, ArgumentKey), (0:1, 0:4, ValidFuncName)], + [(0:6, 0:7, ArgumentKey), (0:1, 0:4, ResolvedFunc)], ); } @@ -1180,7 +1162,7 @@ mod tests { fn parse_multiple_mixed_arguments() { p!("[val: 12pt, key=value]" => [func!("val": (Len(Length::pt(12.0))), { "key" => Id("value") })], [], - [(0:12, 0:15, ArgumentKey), (0:1, 0:4, ValidFuncName)], + [(0:12, 0:15, ArgumentKey), (0:1, 0:4, ResolvedFunc)], ); pval!("a , x=\"b\" , c" => (Id("a"), Id("c")), { "x" => Str("b") }); } @@ -1197,7 +1179,7 @@ mod tests { p!("[val: [hi]]" => [func!("val")], [(0:6, 0:10, "expected argument, found function")], - [(0:1, 0:4, ValidFuncName)], + [(0:1, 0:4, ResolvedFunc)], ); } @@ -1208,7 +1190,7 @@ mod tests { [func!("val": (Bool(true), Id("you")), {})], [(0:10, 0:10, "expected comma"), (0:10, 0:11, "expected argument, found equals sign")], - [(0:1, 0:4, ValidFuncName)], + [(0:1, 0:4, ResolvedFunc)], ); // Unexpected equals. @@ -1223,14 +1205,14 @@ mod tests { [func!("val": (Id("key"), Num(12.0)), {})], [(0:9, 0:9, "expected comma"), (0:9, 0:10, "expected argument, found colon")], - [(0:1, 0:4, ValidFuncName)], + [(0:1, 0:4, ResolvedFunc)], ); // Invalid colon after unkeyable positional argument. p!("[val: true:12]" => [func!("val": (Bool(true), Num(12.0)), {})], [(0:10, 0:10, "expected comma"), (0:10, 0:11, "expected argument, found colon")], - [(0:1, 0:4, ValidFuncName)], + [(0:1, 0:4, ResolvedFunc)], ); } @@ -1274,13 +1256,13 @@ mod tests { // Space before function p!(" [val]" => [(0:0, 0:1, S), (0:1, 0:6, func!((0:1, 0:4, "val")))], [], - [(0:2, 0:5, ValidFuncName)], + [(0:2, 0:5, ResolvedFunc)], ); // Newline before function p!(" \n\r\n[val]" => [(0:0, 2:0, Parbreak), (2:0, 2:5, func!((0:1, 0:4, "val")))], [], - [(2:1, 2:4, ValidFuncName)], + [(2:1, 2:4, ResolvedFunc)], ); // Content before function @@ -1293,7 +1275,7 @@ mod tests { (0:19, 0:20, T("🌎")) ], [], - [(0:7, 0:10, ValidFuncName)], + [(0:7, 0:10, ResolvedFunc)], ); // Nested function @@ -1308,7 +1290,7 @@ mod tests { ])) ], [], - [(0:2, 0:5, ValidFuncName), (1:6, 1:9, ValidFuncName)], + [(0:2, 0:5, ResolvedFunc), (1:6, 1:9, ResolvedFunc)], ); } } diff --git a/src/syntax/scope.rs b/src/syntax/scope.rs index 8fdad6a0..aac2b1b8 100644 --- a/src/syntax/scope.rs +++ b/src/syntax/scope.rs @@ -1,4 +1,4 @@ -//! Scopes containing function parsers. +//! Mapping of function names to function parsers. use std::collections::HashMap; use std::fmt::{self, Debug, Formatter}; @@ -15,33 +15,31 @@ 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>() -> Scope - where F: ParseCall<Meta=()> + DynamicNode + 'static { - Scope { + pub fn new<F>() -> Self + where + F: ParseCall<Meta = ()> + DynamicNode + 'static + { + Self { parsers: HashMap::new(), fallback: make_parser::<F>(()), } } - /// Create a new scope with the standard functions contained. - pub fn with_std() -> Scope { - crate::library::std() - } - - /// Associate the given name with a type that is parseable into a function. + /// Associate the given function name with a dynamic node type. pub fn add<F>(&mut self, name: &str) - where F: ParseCall<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 `()`). + /// 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), - ); + where + F: ParseCall + DynamicNode + 'static + { + self.parsers.insert(name.to_string(), make_parser::<F>(metadata)); } /// Return the parser with the given name if there is one. @@ -57,14 +55,14 @@ impl Scope { impl Debug for Scope { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_set() - .entries(self.parsers.keys()) - .finish() + f.debug_set().entries(self.parsers.keys()).finish() } } fn make_parser<F>(metadata: <F as ParseCall>::Meta) -> Box<CallParser> -where F: ParseCall + DynamicNode + 'static { +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/span.rs b/src/syntax/span.rs index 9b3c7d24..af293718 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -36,13 +36,13 @@ pub struct Spanned<T> { impl<T> Spanned<T> { /// Create a new instance from a value and its span. - pub fn new(v: T, span: Span) -> Spanned<T> { - Spanned { v, span } + pub fn new(v: T, span: Span) -> Self { + Self { v, span } } /// Create a new instance from a value with the zero span. - pub fn zero(v: T) -> Spanned<T> { - Spanned { v, span: Span::ZERO } + pub fn zero(v: T) -> Self { + Self { v, span: Span::ZERO } } /// Access the value. @@ -51,12 +51,12 @@ impl<T> Spanned<T> { } /// Map the value using a function while keeping the span. - pub fn map<V, F>(self, f: F) -> Spanned<V> where F: FnOnce(T) -> V { + pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> { Spanned { v: f(self.v), span: self.span } } /// Maps the span while keeping the value. - pub fn map_span<F>(mut self, f: F) -> Spanned<T> where F: FnOnce(Span) -> Span { + pub fn map_span(mut self, f: impl FnOnce(Span) -> Span) -> Self { self.span = f(self.span); self } @@ -91,35 +91,35 @@ pub struct Span { impl Span { /// The zero span. - pub const ZERO: Span = Span { start: Pos::ZERO, end: Pos::ZERO }; + pub const ZERO: Self = Self { start: Pos::ZERO, end: Pos::ZERO }; /// Create a new span from start and end positions. - pub fn new(start: Pos, end: Pos) -> Span { - Span { start, end } + pub fn new(start: Pos, end: Pos) -> Self { + Self { start, end } } /// Create a span including just a single position. - pub fn at(pos: Pos) -> Span { - Span { start: pos, end: pos } + pub fn at(pos: Pos) -> Self { + Self { start: pos, end: pos } } /// Create a new span with the earlier start and later end position. - pub fn merge(a: Span, b: Span) -> Span { - Span { + pub fn merge(a: Self, b: Self) -> Self { + Self { start: a.start.min(b.start), end: a.end.max(b.end), } } /// Expand a span by merging it with another span. - pub fn expand(&mut self, other: Span) { - *self = Span::merge(*self, other) + pub fn expand(&mut self, other: Self) { + *self = Self::merge(*self, other) } } impl Offset for Span { fn offset(self, by: Pos) -> Self { - Span { + Self { start: self.start.offset(by), end: self.end.offset(by), } @@ -144,31 +144,31 @@ pub struct Pos { impl Pos { /// The line 0, column 0 position. - pub const ZERO: Pos = Pos { line: 0, column: 0 }; + pub const ZERO: Self = Self { line: 0, column: 0 }; /// Create a new position from line and column. - pub fn new(line: usize, column: usize) -> Pos { - Pos { line, column } + pub fn new(line: usize, column: usize) -> Self { + Self { line, column } } } impl Offset for Pos { - fn offset(self, by: Pos) -> Self { + fn offset(self, by: Self) -> Self { by + self } } impl Add for Pos { - type Output = Pos; + type Output = Self; - fn add(self, rhs: Pos) -> Pos { + fn add(self, rhs: Self) -> Self { if rhs.line == 0 { - Pos { + Self { line: self.line, - column: self.column + rhs.column + column: self.column + rhs.column, } } else { - Pos { + Self { line: self.line + rhs.line, column: rhs.column, } @@ -177,16 +177,16 @@ impl Add for Pos { } impl Sub for Pos { - type Output = Pos; + type Output = Self; - fn sub(self, rhs: Pos) -> Pos { + fn sub(self, rhs: Self) -> Self { if self.line == rhs.line { - Pos { + Self { line: 0, - column: self.column - rhs.column + column: self.column - rhs.column, } } else { - Pos { + Self { line: self.line - rhs.line, column: self.column, } diff --git a/src/syntax/test.rs b/src/syntax/test.rs index 504bc334..db7a2de2 100644 --- a/src/syntax/test.rs +++ b/src/syntax/test.rs @@ -2,15 +2,16 @@ 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}; +use super::expr::{Expr, Ident, NamedTuple, Object, Pair, Tuple}; +use super::parsing::{FuncArg, FuncArgs, FuncHeader}; use super::span::Spanned; use super::tokens::Token; -use super::tree::{SyntaxTree, SyntaxNode, DynamicNode}; +use super::tree::{DynamicNode, SyntaxNode, SyntaxTree}; -/// Check whether the expected and found results are the same. pub fn check<T>(src: &str, exp: T, found: T, cmp_spans: bool) -where T: Debug + PartialEq + SpanlessEq { +where + T: Debug + PartialEq + SpanlessEq, +{ let cmp = if cmp_spans { PartialEq::eq } else { SpanlessEq::spanless_eq }; if !cmp(&exp, &found) { println!("source: {:?}", src); @@ -41,7 +42,7 @@ macro_rules! span_vec { } macro_rules! span_item { - (($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => ({ + (($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => {{ use $crate::syntax::span::{Pos, Span, Spanned}; Spanned { span: Span::new( @@ -50,7 +51,7 @@ macro_rules! span_item { ), v: $v } - }); + }}; ($v:expr) => { $crate::syntax::span::Spanned::zero($v) @@ -70,7 +71,7 @@ function! { let cloned = header.clone(); header.args.pos.0.clear(); header.args.key.0.clear(); - DebugFn { + Self { header: cloned, body: parse_maybe_body(body, state, f), } @@ -80,7 +81,7 @@ function! { } /// Compares elements by only looking at values and ignoring spans. -pub trait SpanlessEq<Rhs=Self> { +pub trait SpanlessEq<Rhs = Self> { fn spanless_eq(&self, other: &Rhs) -> bool; } @@ -102,20 +103,21 @@ impl SpanlessEq for SyntaxNode { impl SpanlessEq for DebugFn { fn spanless_eq(&self, other: &DebugFn) -> bool { self.header.spanless_eq(&other.header) - && self.body.spanless_eq(&other.body) + && self.body.spanless_eq(&other.body) } } impl SpanlessEq for FuncHeader { fn spanless_eq(&self, other: &Self) -> bool { - self.name.spanless_eq(&other.name) && self.args.spanless_eq(&other.args) + self.name.spanless_eq(&other.name) + && self.args.spanless_eq(&other.args) } } impl SpanlessEq for FuncArgs { fn spanless_eq(&self, other: &Self) -> bool { self.key.spanless_eq(&other.key) - && self.pos.spanless_eq(&other.pos) + && self.pos.spanless_eq(&other.pos) } } @@ -154,7 +156,7 @@ impl SpanlessEq for Tuple { impl SpanlessEq for NamedTuple { fn spanless_eq(&self, other: &NamedTuple) -> bool { self.name.v == other.name.v - && self.tuple.v.spanless_eq(&other.tuple.v) + && self.tuple.v.spanless_eq(&other.tuple.v) } } @@ -173,7 +175,7 @@ impl SpanlessEq for Pair { impl<T: SpanlessEq> SpanlessEq for Vec<T> { fn spanless_eq(&self, other: &Vec<T>) -> bool { self.len() == other.len() - && self.iter().zip(other).all(|(x, y)| x.spanless_eq(&y)) + && self.iter().zip(other).all(|(x, y)| x.spanless_eq(&y)) } } diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 1ea11449..ef249471 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -9,7 +9,6 @@ use super::span::{Pos, Span, Spanned}; use Token::*; use TokenMode::*; - /// A minimal semantic entity of source code. #[derive(Debug, Copy, Clone, PartialEq)] pub enum Token<'s> { @@ -71,14 +70,14 @@ pub enum Token<'s> { /// a String. The escaping is done later in the parser. string: &'s str, /// Whether the closing quote was present. - terminated: bool + terminated: bool, }, + /// A boolean in a function header: `true | false`. + ExprBool(bool), /// A number in a function header: `3.14`. ExprNumber(f64), /// A length in a function header: `12pt`. ExprLength(Length), - /// A boolean in a function header: `true | false`. - ExprBool(bool), /// A hex value in a function header: `#20d82a`. ExprHex(&'s str), /// A plus in a function header, signifying the addition of expressions. @@ -130,9 +129,9 @@ impl<'s> Token<'s> { Equals => "equals sign", ExprIdent(_) => "identifier", ExprStr { .. } => "string", + ExprBool(_) => "bool", ExprNumber(_) => "number", ExprLength(_) => "length", - ExprBool(_) => "bool", ExprHex(_) => "hex value", Plus => "plus", Hyphen => "minus", @@ -173,8 +172,8 @@ impl<'s> Tokens<'s> { /// /// The first token's span starts an the given `offset` position instead of /// the zero position. - pub fn new(src: &'s str, offset: Pos, mode: TokenMode) -> Tokens<'s> { - Tokens { + pub fn new(src: &'s str, offset: Pos, mode: TokenMode) -> Self { + Self { src, mode, iter: src.chars().peekable(), @@ -200,7 +199,7 @@ impl<'s> Iterator for Tokens<'s> { type Item = Spanned<Token<'s>>; /// Parse the next token in the source code. - fn next(&mut self) -> Option<Spanned<Token<'s>>> { + fn next(&mut self) -> Option<Self::Item> { let start = self.pos(); let first = self.eat()?; @@ -366,7 +365,7 @@ impl<'s> Tokens<'s> { } let end = self.index(); - (&self.src[start .. end], terminated) + (&self.src[start..end], terminated) } fn read_string(&mut self) -> Token<'s> { @@ -404,7 +403,7 @@ impl<'s> Tokens<'s> { Some(c) if is_escapable(c) => { let index = self.index(); self.eat(); - Text(&self.src[index .. index + c.len_utf8()]) + Text(&self.src[index..index + c.len_utf8()]) } Some(c) if c.is_whitespace() => Backslash, Some(_) => Text("\\"), @@ -442,13 +441,13 @@ impl<'s> Tokens<'s> { /// Returns the string from the index where this was called offset by /// `offset_start` to the end offset by `offset_end`. The end is before or /// after the match depending on `eat_match`. - fn read_string_until<F>( + fn read_string_until( &mut self, - mut f: F, + mut f: impl FnMut(char) -> bool, eat_match: bool, offset_start: isize, offset_end: isize, - ) -> (&'s str, bool) where F: FnMut(char) -> bool { + ) -> (&'s str, bool) { let start = ((self.index() as isize) + offset_start) as usize; let mut matched = false; @@ -469,7 +468,7 @@ impl<'s> Tokens<'s> { end = ((end as isize) + offset_end) as usize; } - (&self.src[start .. end], matched) + (&self.src[start..end], matched) } fn eat(&mut self) -> Option<char> { @@ -493,7 +492,7 @@ impl<'s> Tokens<'s> { fn parse_percentage(text: &str) -> Option<f64> { if text.ends_with('%') { - text[.. text.len() - 1].parse::<f64>().ok() + text[..text.len() - 1].parse::<f64>().ok() } else { None } @@ -503,7 +502,7 @@ fn parse_percentage(text: &str) -> Option<f64> { pub fn is_newline_char(character: char) -> bool { match character { // Line Feed, Vertical Tab, Form Feed, Carriage Return. - '\x0A' ..= '\x0D' => true, + '\x0A'..='\x0D' => true, // Next Line, Line Separator, Paragraph Separator. '\u{0085}' | '\u{2028}' | '\u{2029}' => true, _ => false, @@ -544,15 +543,15 @@ mod tests { LeftParen as LP, RightParen as RP, LeftBrace as LB, RightBrace as RB, ExprIdent as Id, + ExprBool as Bool, ExprNumber as Num, ExprLength as Len, - ExprBool as Bool, ExprHex as Hex, - Text as T, Plus, Hyphen as Min, - Star, Slash, + Star, + Text as T, }; /// Test whether the given string tokenizes into the given list of tokens. diff --git a/src/syntax/tree.rs b/src/syntax/tree.rs index 41a03fae..28997e7c 100644 --- a/src/syntax/tree.rs +++ b/src/syntax/tree.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; use crate::layout::Layout; use super::span::SpanVec; -/// A list of nodes which forms a tree together with the nodes' children. +/// A collection of nodes which form a tree together with the nodes' children. pub type SyntaxTree = SpanVec<SyntaxNode>; /// A syntax node, which encompasses a single logical entity of parsed source @@ -27,7 +27,7 @@ pub enum SyntaxNode { ToggleItalic, /// Bolder was enabled / disabled. ToggleBolder, - /// A subtree, typically a function invocation. + /// A dynamic node, create through function invocations in source code. Dyn(Box<dyn DynamicNode>), } @@ -65,13 +65,16 @@ pub trait DynamicNode: Debug + Layout { impl dyn DynamicNode { /// Downcast this dynamic node to a concrete node. - pub fn downcast<N>(&self) -> Option<&N> where N: DynamicNode + 'static { - self.as_any().downcast_ref::<N>() + pub fn downcast<T>(&self) -> Option<&T> + where + T: DynamicNode + 'static, + { + self.as_any().downcast_ref::<T>() } } impl PartialEq for dyn DynamicNode { - fn eq(&self, other: &dyn DynamicNode) -> bool { + fn eq(&self, other: &Self) -> bool { self.dyn_eq(other) } } @@ -82,7 +85,10 @@ impl Clone for Box<dyn DynamicNode> { } } -impl<T> DynamicNode for T where T: Debug + PartialEq + Clone + Layout + 'static { +impl<T> DynamicNode for T +where + T: Debug + PartialEq + Clone + Layout + 'static, +{ fn as_any(&self) -> &dyn Any { self } diff --git a/src/syntax/value.rs b/src/syntax/value.rs index 5c264ca0..b7211eaf 100644 --- a/src/syntax/value.rs +++ b/src/syntax/value.rs @@ -2,18 +2,20 @@ use fontdock::{FontStyle, FontWeight, FontWidth}; -use crate::Feedback; use crate::layout::prelude::*; use crate::length::{Length, ScaleLength}; use crate::paper::Paper; -use super::span::Spanned; +use crate::Feedback; use super::expr::*; +use super::span::Spanned; /// Value types are used to extract values from functions, tuples and /// objects. They represent the value part of an argument. +/// +/// # Example /// ```typst -/// [func: value, key=value] -/// ^^^^^ ^^^^^ +/// [func: 12pt, key="these are both values"] +/// ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` pub trait Value: Sized { /// Try to parse this value from an expression. @@ -53,8 +55,8 @@ macro_rules! match_value { match_value!(Expr, "expression", e => e); match_value!(Ident, "identifier", Expr::Ident(i) => i); match_value!(String, "string", Expr::Str(s) => s); -match_value!(f64, "number", Expr::Number(n) => n); match_value!(bool, "bool", Expr::Bool(b) => b); +match_value!(f64, "number", Expr::Number(n) => n); match_value!(Length, "length", Expr::Length(l) => l); match_value!(Tuple, "tuple", Expr::Tuple(t) => t); match_value!(Object, "object", Expr::Object(o) => o); @@ -63,7 +65,7 @@ match_value!(ScaleLength, "number or length", Expr::Number(scale) => ScaleLength::Scaled(scale), ); -/// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and implements +/// A value type that matches identifiers and strings and implements /// `Into<String>`. pub struct StringLike(pub String); @@ -101,7 +103,7 @@ macro_rules! ident_value { } ident_value!(Dir, "direction", |s| match s { - "ltr" => Some(LTT), + "ltr" => Some(LTR), "rtl" => Some(RTL), "ttb" => Some(TTB), "btt" => Some(BTT), @@ -109,11 +111,11 @@ ident_value!(Dir, "direction", |s| match s { }); ident_value!(SpecAlign, "alignment", |s| match s { - "left" => Some(SpecAlign::Left), - "right" => Some(SpecAlign::Right), - "top" => Some(SpecAlign::Top), - "bottom" => Some(SpecAlign::Bottom), - "center" => Some(SpecAlign::Center), + "left" => Some(Self::Left), + "right" => Some(Self::Right), + "top" => Some(Self::Top), + "bottom" => Some(Self::Bottom), + "center" => Some(Self::Center), _ => None, }); @@ -127,7 +129,7 @@ impl Value for FontWeight { const MIN: u16 = 100; const MAX: u16 = 900; - Some(FontWeight(if weight < MIN as f64 { + Some(Self(if weight < MIN as f64 { error!(@f, expr.span, "the minimum font weight is {}", MIN); MIN } else if weight > MAX as f64 { @@ -163,7 +165,7 @@ impl Value for FontWidth { const MIN: u16 = 1; const MAX: u16 = 9; - FontWidth::new(if width < MIN as f64 { + Self::new(if width < MIN as f64 { error!(@f, expr.span, "the minimum font width is {}", MIN); MIN } else if width > MAX as f64 { |
