lol_html/selectors_vm/
program.rs

1use super::attribute_matcher::AttributeMatcher;
2use super::compiler::{CompiledAttributeExpr, CompiledLocalNameExpr};
3use super::SelectorState;
4use crate::html::LocalName;
5use hashbrown::HashSet;
6use std::hash::Hash;
7use std::ops::Range;
8
9pub(crate) type AddressRange = Range<usize>;
10
11#[derive(Debug, PartialEq, Eq)]
12pub(crate) struct ExecutionBranch<P>
13where
14    P: Hash + Eq,
15{
16    pub matched_payload: HashSet<P>,
17    pub jumps: Option<AddressRange>,
18    pub hereditary_jumps: Option<AddressRange>,
19}
20
21/// The result of trying to execute an instruction without having parsed all attributes
22pub(crate) enum TryExecResult<'i, P>
23where
24    P: Hash + Eq,
25{
26    /// A successful match, contains the branch to move to
27    Branch(&'i ExecutionBranch<P>),
28    /// A partially successful match, but requires attributes to complete
29    AttributesRequired,
30    /// A failed match, doesn't require attributes to complete
31    Fail,
32}
33
34pub(crate) struct Instruction<P>
35where
36    P: Hash + Eq,
37{
38    pub associated_branch: ExecutionBranch<P>,
39    pub local_name_exprs: Box<[CompiledLocalNameExpr]>,
40    pub attribute_exprs: Box<[CompiledAttributeExpr]>,
41}
42
43impl<P> Instruction<P>
44where
45    P: Hash + Eq,
46{
47    pub fn try_exec_without_attrs<'i>(
48        &'i self,
49        state: &SelectorState<'_>,
50        local_name: &LocalName<'_>,
51    ) -> TryExecResult<'i, P> {
52        if self.local_name_exprs.iter().all(|e| e(state, local_name)) {
53            if self.attribute_exprs.is_empty() {
54                TryExecResult::Branch(&self.associated_branch)
55            } else {
56                TryExecResult::AttributesRequired
57            }
58        } else {
59            TryExecResult::Fail
60        }
61    }
62
63    pub fn complete_exec_with_attrs<'i>(
64        &'i self,
65        state: &SelectorState<'_>,
66        attr_matcher: &AttributeMatcher<'_>,
67    ) -> Option<&'i ExecutionBranch<P>> {
68        if self.attribute_exprs.iter().all(|e| e(state, attr_matcher)) {
69            Some(&self.associated_branch)
70        } else {
71            None
72        }
73    }
74
75    pub fn exec<'i>(
76        &'i self,
77        state: &SelectorState<'_>,
78        local_name: &LocalName<'_>,
79        attr_matcher: &AttributeMatcher<'_>,
80    ) -> Option<&'i ExecutionBranch<P>> {
81        let is_match = self.local_name_exprs.iter().all(|e| e(state, local_name))
82            && self.attribute_exprs.iter().all(|e| e(state, attr_matcher));
83
84        if is_match {
85            Some(&self.associated_branch)
86        } else {
87            None
88        }
89    }
90}
91
92pub(crate) struct Program<P>
93where
94    P: Hash + Eq,
95{
96    pub instructions: Box<[Instruction<P>]>,
97    pub entry_points: AddressRange,
98    /// Enables tracking child types for nth-of-type selectors.
99    /// This is disabled if no nth-of-type selectors are used in the program.
100    pub enable_nth_of_type: bool,
101}