diff --git a/Cargo.toml b/Cargo.toml index bd8b8daf5..60326cd55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "askama_derive", "askama_escape", "askama_mendes", + "askama_parser", "askama_rocket", "askama_tide", "askama_warp", @@ -18,5 +19,6 @@ default-members = [ "askama", "askama_derive", "askama_escape", + "askama_parser", "testing", ] diff --git a/askama_derive/Cargo.toml b/askama_derive/Cargo.toml index 0a4d9a6f2..d39f2a3d8 100644 --- a/askama_derive/Cargo.toml +++ b/askama_derive/Cargo.toml @@ -31,6 +31,7 @@ with-tide = [] with-warp = [] [dependencies] +askama_parser = { version = "0.1", path = "../askama_parser" } mime = "0.3" mime_guess = "2" proc-macro2 = "1" diff --git a/askama_derive/src/config.rs b/askama_derive/src/config.rs index f9109e545..c54df1324 100644 --- a/askama_derive/src/config.rs +++ b/askama_derive/src/config.rs @@ -6,8 +6,8 @@ use std::{env, fs}; #[cfg(feature = "serde")] use serde::Deserialize; -use crate::parser::Syntax; use crate::CompileError; +use askama_parser::Syntax; #[derive(Debug)] pub(crate) struct Config<'a> { diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index 1f2e6e04d..51d8d61e7 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -1,11 +1,11 @@ use crate::config::{get_template_source, read_config_file, Config, WhitespaceHandling}; use crate::heritage::{Context, Heritage}; use crate::input::{Print, Source, TemplateInput}; -use crate::parser::{ +use crate::CompileError; +use askama_parser::{ parse, Block, BlockDef, Call, Cond, CondTest, Expr, Lit, Loop, Match, Node, Raw, Tag, Target, When, Whitespace, Ws, }; -use crate::CompileError; use proc_macro::TokenStream; use quote::{quote, ToTokens}; diff --git a/askama_derive/src/heritage.rs b/askama_derive/src/heritage.rs index a34e1a726..f0b4385aa 100644 --- a/askama_derive/src/heritage.rs +++ b/askama_derive/src/heritage.rs @@ -2,8 +2,8 @@ use std::collections::HashMap; use std::path::{Path, PathBuf}; use crate::config::Config; -use crate::parser::{Block, BlockDef, Cond, Loop, Macro, Match, Node, Tag, When}; use crate::CompileError; +use askama_parser::{Block, BlockDef, Cond, Loop, Macro, Match, Node, Tag, When}; pub(crate) struct Heritage<'a> { pub(crate) root: &'a Context<'a>, diff --git a/askama_derive/src/input.rs b/askama_derive/src/input.rs index be425a3c1..f80cb5bff 100644 --- a/askama_derive/src/input.rs +++ b/askama_derive/src/input.rs @@ -1,7 +1,7 @@ use crate::config::Config; use crate::generator::TemplateArgs; -use crate::parser::Syntax; use crate::CompileError; +use askama_parser::Syntax; use std::path::{Path, PathBuf}; use std::str::FromStr; diff --git a/askama_derive/src/lib.rs b/askama_derive/src/lib.rs index 2682df8d3..a56bf290f 100644 --- a/askama_derive/src/lib.rs +++ b/askama_derive/src/lib.rs @@ -11,7 +11,6 @@ mod config; mod generator; mod heritage; mod input; -mod parser; #[proc_macro_derive(Template, attributes(template))] pub fn derive_template(input: TokenStream) -> TokenStream { @@ -52,8 +51,8 @@ impl From for CompileError { } } -impl From for CompileError { - fn from(e: parser::ParseError) -> Self { +impl From for CompileError { + fn from(e: askama_parser::ParseError) -> Self { Self::new(e.to_string()) } } diff --git a/askama_parser/Cargo.toml b/askama_parser/Cargo.toml new file mode 100644 index 000000000..c38699e7e --- /dev/null +++ b/askama_parser/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "askama_parser" +version = "0.1.0" +description = "Askama template syntax parser" +homepage = "https://github.com/djc/askama" +repository = "https://github.com/djc/askama" +license = "MIT OR Apache-2.0" +workspace = ".." +readme = "../README.md" +edition = "2021" +rust-version = "1.58" + +[dependencies] +nom = "7" diff --git a/askama_derive/src/parser/expr.rs b/askama_parser/src/expr.rs similarity index 98% rename from askama_derive/src/parser/expr.rs rename to askama_parser/src/expr.rs index 70d963c8d..c5b444853 100644 --- a/askama_derive/src/parser/expr.rs +++ b/askama_parser/src/expr.rs @@ -16,7 +16,7 @@ use super::{ /// An Askama expression. #[derive(Debug, PartialEq)] -pub(crate) enum Expr<'a> { +pub enum Expr<'a> { /// A boolean literal. BoolLit(&'a str), /// A numeric literal. @@ -66,7 +66,7 @@ impl Expr<'_> { /// Returns `true` if enough assumptions can be made, /// to determine that `self` is copyable. - pub(crate) fn is_copyable(&self) -> bool { + pub fn is_copyable(&self) -> bool { self.is_copyable_within_op(false) } @@ -94,7 +94,7 @@ impl Expr<'_> { } /// Returns `true` if this is an `Attr` where the `obj` is `"self"`. - pub(crate) fn is_attr_self(&self) -> bool { + pub fn is_attr_self(&self) -> bool { match self { Expr::Attr(obj, _) if matches!(obj.as_ref(), Expr::Var("self")) => true, Expr::Attr(obj, _) if matches!(obj.as_ref(), Expr::Attr(..)) => obj.is_attr_self(), @@ -105,7 +105,7 @@ impl Expr<'_> { /// Returns `true` if the outcome of this expression may be used multiple times in the same /// `write!()` call, without evaluating the expression again, i.e. the expression should be /// side-effect free. - pub(crate) fn is_cacheable(&self) -> bool { + pub fn is_cacheable(&self) -> bool { match self { // Literals are the definition of pure: Expr::BoolLit(_) => true, diff --git a/askama_derive/src/parser/mod.rs b/askama_parser/src/lib.rs similarity index 94% rename from askama_derive/src/parser/mod.rs rename to askama_parser/src/lib.rs index efc1538ce..173f30e8b 100644 --- a/askama_derive/src/parser/mod.rs +++ b/askama_parser/src/lib.rs @@ -23,8 +23,8 @@ use nom::multi::separated_list1; use nom::sequence::{delimited, pair, tuple}; use nom::{error_position, AsChar, IResult, InputTakeAtPosition}; -pub(crate) use self::expr::Expr; -pub(crate) use self::node::{ +pub use self::expr::Expr; +pub use self::node::{ Block, BlockDef, Call, Cond, CondTest, Lit, Loop, Macro, Match, Node, Raw, Tag, Target, When, }; @@ -35,19 +35,19 @@ mod tests; /// Askama template syntax configuration. #[derive(Debug)] -pub(crate) struct Syntax<'a> { +pub struct Syntax<'a> { /// Defaults to `"{%"`. - pub(crate) block_start: &'a str, + pub block_start: &'a str, /// Defaults to `"%}"`. - pub(crate) block_end: &'a str, + pub block_end: &'a str, /// Defaults to `"{{"`. - pub(crate) expr_start: &'a str, + pub expr_start: &'a str, /// Defaults to `"}}"`. - pub(crate) expr_end: &'a str, + pub expr_end: &'a str, /// Defaults to `"{#"`. - pub(crate) comment_start: &'a str, + pub comment_start: &'a str, /// Defaults to `"#}"`. - pub(crate) comment_end: &'a str, + pub comment_end: &'a str, } impl Default for Syntax<'static> { @@ -65,7 +65,7 @@ impl Default for Syntax<'static> { /// Whitespace preservation or suppression. #[derive(Clone, Copy, Debug, PartialEq)] -pub(crate) enum Whitespace { +pub enum Whitespace { Preserve, Suppress, Minimize, @@ -73,11 +73,11 @@ pub(crate) enum Whitespace { /// Whitespace suppression for a `Tag` or `Block`. #[derive(Clone, Copy, Debug, Default, PartialEq)] -pub(crate) struct Ws { +pub struct Ws { /// Handling of trailing whitespace on literal text at a transition in to Askama. - pub(crate) flush: Option, + pub flush: Option, /// Handling of leading whitespace on literal text at a transition out of Askama. - pub(crate) prepare: Option, + pub prepare: Option, } impl Ws { @@ -127,7 +127,7 @@ impl From for Whitespace { /// Parse template source to an abstract syntax tree. /// /// Tries to parse the provided template string using the given syntax. -pub(crate) fn parse<'a>(src: &'a str, syntax: &'a Syntax<'_>) -> Result, ParseError> { +pub fn parse<'a>(src: &'a str, syntax: &'a Syntax<'_>) -> Result, ParseError> { let state = State::new(syntax); let mut p = all_consuming(complete(|i| Node::parse(i, &state))); match p(src) { @@ -164,21 +164,20 @@ pub(crate) fn parse<'a>(src: &'a str, syntax: &'a Syntax<'_>) -> Result usize { + pub fn line(&self) -> usize { self.row } /// The column number in the source where the error was identified. - pub(crate) fn column(&self) -> usize { + pub fn column(&self) -> usize { self.column } } diff --git a/askama_derive/src/parser/node.rs b/askama_parser/src/node.rs similarity index 94% rename from askama_derive/src/parser/node.rs rename to askama_parser/src/node.rs index 5cca11ec4..304f0cf0c 100644 --- a/askama_derive/src/parser/node.rs +++ b/askama_parser/src/node.rs @@ -21,16 +21,16 @@ use super::{ /// /// This represents both the top-level block of a template and all sub-blocks of statement nodes. #[derive(Debug, PartialEq)] -pub(crate) struct Block<'a> { +pub struct Block<'a> { /// The nodes within the block. - pub(crate) nodes: Vec>, + pub nodes: Vec>, /// Whitespace suppression for the inside of the block. - pub(crate) ws: Ws, + pub ws: Ws, } impl<'a> Block<'a> { #[cfg(test)] - pub(crate) fn with_whitespace(ws: Ws) -> Self { + pub fn with_whitespace(ws: Ws) -> Self { Block { nodes: vec![], ws } } } @@ -43,7 +43,7 @@ impl<'a> PartialEq>> for Block<'a> { /// An Askama template abstract syntax tree node. #[derive(Debug, PartialEq)] -pub(crate) enum Node<'a> { +pub enum Node<'a> { /// Literal text to output directly. Lit(Lit<'a>), /// An Askama tag, either a comment, expression, or statement. @@ -62,7 +62,7 @@ pub(crate) enum Node<'a> { /// may or may not have a matching end tag, depending on the type of statement. /// Statements with child `Block`s always require an end tag. #[derive(Debug, PartialEq)] -pub(crate) enum Tag<'a> { +pub enum Tag<'a> { /// A block comment. /// /// ```ignore @@ -182,7 +182,7 @@ pub(crate) enum Tag<'a> { /// The Askama equivalent of a Rust pattern, the target of a match or assignment. #[derive(Debug, PartialEq)] -pub(crate) enum Target<'a> { +pub enum Target<'a> { /// Bind the value to a name. Name(&'a str), /// Destructure a tuple value. @@ -203,104 +203,104 @@ pub(crate) enum Target<'a> { /// A literal bit of text to output directly. #[derive(Debug, PartialEq)] -pub(crate) struct Lit<'a> { +pub struct Lit<'a> { /// White space preceeding the text. - pub(crate) lws: &'a str, + pub lws: &'a str, /// The literal text itself. - pub(crate) val: &'a str, + pub val: &'a str, /// White space following the text. - pub(crate) rws: &'a str, + pub rws: &'a str, } /// A raw block to output directly. #[derive(Debug, PartialEq)] -pub(crate) struct Raw<'a> { +pub struct Raw<'a> { /// The content of the raw block. - pub(crate) lit: Lit<'a>, + pub lit: Lit<'a>, /// Whitespace suppression for the inside of the block. - pub(crate) ws: Ws, + pub ws: Ws, } /// A macro call statement. #[derive(Debug, PartialEq)] -pub(crate) struct Call<'a> { +pub struct Call<'a> { /// If the macro is imported, the scope name. - pub(crate) scope: Option<&'a str>, + pub scope: Option<&'a str>, /// The name of the macro to call. - pub(crate) name: &'a str, + pub name: &'a str, /// The arguments to the macro. - pub(crate) args: Vec>, + pub args: Vec>, } /// A match statement. #[derive(Debug, PartialEq)] -pub(crate) struct Match<'a> { +pub struct Match<'a> { /// The expression to match against. - pub(crate) expr: Expr<'a>, + pub expr: Expr<'a>, /// Each of the match arms, with a pattern and a body. - pub(crate) arms: Vec>, + pub arms: Vec>, } /// A single arm of a match statement. #[derive(Debug, PartialEq)] -pub(crate) struct When<'a> { +pub struct When<'a> { /// The target pattern to match. - pub(crate) target: Target<'a>, + pub target: Target<'a>, /// Body of the match arm. - pub(crate) block: Block<'a>, + pub block: Block<'a>, } /// A for loop syntax node. #[derive(Debug, PartialEq)] -pub(crate) struct Loop<'a> { +pub struct Loop<'a> { /// The variable of iteration within the loop. - pub(crate) var: Target<'a>, + pub var: Target<'a>, /// The collection to iterate over. - pub(crate) iter: Expr<'a>, + pub iter: Expr<'a>, /// An optional condition, which if it evaluates to false should skip that iteration. - pub(crate) cond: Option>, + pub cond: Option>, /// The body of the loop. - pub(crate) body: Block<'a>, + pub body: Block<'a>, /// The else block of the loop, invoked if the collection is empty. - pub(crate) else_block: Block<'a>, + pub else_block: Block<'a>, } /// A macro definition. #[derive(Debug, PartialEq)] -pub(crate) struct Macro<'a> { +pub struct Macro<'a> { /// The name of the macro. - pub(crate) name: &'a str, + pub name: &'a str, /// Names of each of the macro's parameters. - pub(crate) args: Vec<&'a str>, + pub args: Vec<&'a str>, /// The body of the macro. - pub(crate) block: Block<'a>, + pub block: Block<'a>, } /// A block statement, either a definition or a reference. #[derive(Debug, PartialEq)] -pub(crate) struct BlockDef<'a> { +pub struct BlockDef<'a> { /// The name of the block. - pub(crate) name: &'a str, + pub name: &'a str, /// The contents of the block. - pub(crate) block: Block<'a>, + pub block: Block<'a>, } /// A single branch of a conditional statement. #[derive(Debug, PartialEq)] -pub(crate) struct Cond<'a> { +pub struct Cond<'a> { /// The test for this branch, or `None` for the `else` branch. - pub(crate) test: Option>, + pub test: Option>, /// Body of this conditional branch. - pub(crate) block: Block<'a>, + pub block: Block<'a>, } /// An if or if let condition. #[derive(Debug, PartialEq)] -pub(crate) struct CondTest<'a> { +pub struct CondTest<'a> { /// For an if let, the assignment target. - pub(crate) target: Option>, + pub target: Option>, /// The condition expression to evaluate. - pub(crate) expr: Expr<'a>, + pub expr: Expr<'a>, } impl Node<'_> { diff --git a/askama_derive/src/parser/tests.rs b/askama_parser/src/tests.rs similarity index 99% rename from askama_derive/src/parser/tests.rs rename to askama_parser/src/tests.rs index 5df05831c..606389fa4 100644 --- a/askama_derive/src/parser/tests.rs +++ b/askama_parser/src/tests.rs @@ -1,4 +1,4 @@ -use crate::parser::{Block, Expr, Lit, Node, Syntax, Tag, Target, Whitespace, Ws}; +use crate::{Block, Expr, Lit, Node, Syntax, Tag, Target, Whitespace, Ws}; fn check_ws_split(s: &str, res: &(&str, &str, &str)) { let Lit { lws, val, rws } = super::split_ws_parts(s);