diff --git a/filter-types/src/context.rs b/filter-types/src/context.rs index b06d272..7988cf5 100644 --- a/filter-types/src/context.rs +++ b/filter-types/src/context.rs @@ -52,7 +52,20 @@ impl<'a, X> Bindings<'a, X> { ) } - /// Conversts these [`Bindings`] into a [`Value`]. Specifically, it makes + /// Converts a [`Bindings`] into a [`Bindings`]. This is a lossless operation + /// because a [`Value`] can always be converted into a [`Value`]. + #[must_use] + pub fn from_data(bindings: Bindings<'a, Data>) -> Self { + Self( + bindings + .0 + .into_iter() + .map(|(k, v)| (k, Value::from_data(v))) + .collect(), + ) + } + + /// Converts these [`Bindings`] into a [`Value`]. Specifically, it makes /// them a key-value `Structure` by taking ownership of the keys and mapping /// them to the mapped values. #[must_use] diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 909c212..f145d76 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -1,6 +1,5 @@ pub mod ast; mod parser; -mod repl; mod scanner; pub use parser::{ParseError, Parser}; diff --git a/src/frontend/parser.rs b/src/frontend/parser.rs index f680df8..70633b3 100644 --- a/src/frontend/parser.rs +++ b/src/frontend/parser.rs @@ -89,7 +89,7 @@ impl<'a> Parser<'a> { Ok(vec) } - fn parse_statement(&mut self) -> Result> { + pub(crate) fn parse_statement(&mut self) -> Result> { let id = self.try_eat(Token::Id)?.value; self.try_eat(Token::Colon)?; let value = self.parse_rvalue()?; diff --git a/src/frontend/repl.rs b/src/frontend/repl.rs deleted file mode 100644 index cc18ac3..0000000 --- a/src/frontend/repl.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[derive(Debug, Clone)] -pub struct Repl {} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 65570f1..887fae1 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -14,17 +14,15 @@ pub mod filter; pub use scrapelect_filter_types::{Error, MessageExt, Result, WrapExt}; #[derive(Debug)] -pub struct Interpreter<'ast> { +pub struct Interpreter { client: reqwest::Client, - ast: &'ast [Statement<'ast>], } -impl<'ast> Interpreter<'ast> { +impl Interpreter { #[must_use] #[inline] - pub fn new(ast: &'ast [Statement<'ast>]) -> Self { + pub fn new() -> Self { Self::with_client( - ast, reqwest::Client::builder() .user_agent(concat!( env!("CARGO_PKG_NAME"), @@ -38,14 +36,18 @@ impl<'ast> Interpreter<'ast> { #[must_use] #[inline] - pub const fn with_client(ast: &'ast [Statement<'ast>], client: reqwest::Client) -> Self { - Self { ast, client } + pub const fn with_client(client: reqwest::Client) -> Self { + Self { client } } #[inline] - pub async fn interpret(&self, root_url: Url) -> Result> { + pub async fn interpret<'ast>( + &self, + statements: &[Statement<'ast>], + root_url: Url, + ) -> Result> { let html = self.get_html(&root_url).await?; - self.interpret_block(html.root_element(), self.ast, None, root_url) + self.interpret_block(html.root_element(), statements, None, root_url) .await } @@ -69,10 +71,10 @@ impl<'ast> Interpreter<'ast> { Ok(scraper::Html::parse_document(&text)) } - async fn interpret_block( + async fn interpret_block<'ast>( &self, element: scraper::ElementRef<'_>, - statements: &'ast [Statement<'ast>], + statements: &[Statement<'ast>], parent: Option<&ElementContext<'ast, '_>>, url: Url, ) -> Result> { @@ -85,9 +87,9 @@ impl<'ast> Interpreter<'ast> { Ok(ctx.bindings.into_data()) } - async fn interpret_statement( + async fn interpret_statement<'ast>( &self, - statement: &'ast Statement<'ast>, + statement: &Statement<'ast>, ctx: &mut ElementContext<'ast, '_>, ) -> Result<()> { let inner = || async move { @@ -110,9 +112,9 @@ impl<'ast> Interpreter<'ast> { }) } - async fn interpret_element( + async fn interpret_element<'ast>( &self, - element: &'ast Element<'ast>, + element: &Element<'ast>, ctx: &mut ElementContext<'ast, '_>, ) -> Result { let selector_str = &element.selector.to_string(); @@ -165,10 +167,10 @@ impl<'ast> Interpreter<'ast> { .wrap_with(|| format!("note: occurred while evaluating element block `{selector_str}`")) } - fn apply_filters<'ctx>( + fn apply_filters<'a, 'ast: 'a, 'ctx>( &self, value: EValue<'ctx>, - mut filters: impl Iterator>, + mut filters: impl Iterator>, ctx: &mut ElementContext<'ast, 'ctx>, ) -> Result> { filters @@ -208,9 +210,9 @@ impl<'ast> Interpreter<'ast> { .map(EValue::from) } - fn eval_inline<'ctx>( + fn eval_inline<'ast, 'ctx>( &self, - inline: &'ast Inline<'ast>, + inline: &Inline<'ast>, ctx: &mut ElementContext<'ast, 'ctx>, ) -> Result> { self.apply_filters( @@ -263,7 +265,7 @@ pub async fn interpret_string_harness( let statements = crate::frontend::Parser::new(program).parse()?; let html = scraper::Html::parse_document(html); let statements = Box::leak(Box::new(statements)); - let interpreter = Interpreter::new(statements); + let interpreter = Interpreter::new(); interpreter // TODO: url hack .interpret_block( @@ -290,7 +292,7 @@ mod tests { let html = scraper::Html::parse_document(&input); - let result = super::Interpreter::new(&ast) + let result = super::Interpreter::new() .interpret_block( html.root_element(), &ast, diff --git a/src/main.rs b/src/main.rs index 19b3ab9..4d0e218 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,9 +46,9 @@ async fn main() -> anyhow::Result<()> { .parse() .with_context(|| format!("parse error in {}:", run_args.file.display()))?; - let interpreter = Interpreter::new(&ast); + let interpreter = Interpreter::new(); - let results = interpreter.interpret(run_args.url).await?; + let results = interpreter.interpret(&ast, run_args.url).await?; println!("{}", serde_json::to_string_pretty(&results)?); }