diff --git a/boa_engine/src/syntax/ast/declaration/mod.rs b/boa_engine/src/syntax/ast/declaration/mod.rs index 1b9fb43b19a..ef45001da86 100644 --- a/boa_engine/src/syntax/ast/declaration/mod.rs +++ b/boa_engine/src/syntax/ast/declaration/mod.rs @@ -20,10 +20,12 @@ use super::{ ContainsSymbol, }; use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; use tap::Tap; mod variable; +use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; pub use variable::*; /// The `Declaration` Parse Node. @@ -165,3 +167,21 @@ impl ToIndentedString for Declaration { } } } + +impl VisitWith for Declaration { + fn visit_with<'a>(&'a self, _visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + // TODO implement + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a>(&'a mut self, _visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + // TODO implement + ControlFlow::Continue(()) + } +} diff --git a/boa_engine/src/syntax/ast/mod.rs b/boa_engine/src/syntax/ast/mod.rs index 360f45a16d5..6f4d3e87f40 100644 --- a/boa_engine/src/syntax/ast/mod.rs +++ b/boa_engine/src/syntax/ast/mod.rs @@ -24,6 +24,7 @@ pub mod keyword; pub mod pattern; pub mod property; pub mod statement; +pub mod visitor; use boa_interner::{Interner, ToIndentedString, ToInternedString}; diff --git a/boa_engine/src/syntax/ast/statement/mod.rs b/boa_engine/src/syntax/ast/statement/mod.rs index c097e04627b..92f3f3b572e 100644 --- a/boa_engine/src/syntax/ast/statement/mod.rs +++ b/boa_engine/src/syntax/ast/statement/mod.rs @@ -28,7 +28,9 @@ pub use self::{ switch::{Case, Switch}, throw::Throw, }; +use core::ops::ControlFlow; +use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use rustc_hash::FxHashSet; use tap::Tap; @@ -313,3 +315,21 @@ impl ToIndentedString for Statement { buf } } + +impl VisitWith for Statement { + fn visit_with<'a>(&'a self, _visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + // TODO implement + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a>(&'a mut self, _visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + // TODO implement + ControlFlow::Continue(()) + } +} diff --git a/boa_engine/src/syntax/ast/statement_list/mod.rs b/boa_engine/src/syntax/ast/statement_list/mod.rs index f9f0f5297c7..d761d7dddd7 100644 --- a/boa_engine/src/syntax/ast/statement_list/mod.rs +++ b/boa_engine/src/syntax/ast/statement_list/mod.rs @@ -1,8 +1,10 @@ //! Statement list node. use super::{declaration::Binding, Declaration}; +use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; use crate::syntax::ast::{expression::Identifier, statement::Statement, ContainsSymbol}; use boa_interner::{Interner, ToIndentedString}; +use core::ops::ControlFlow; use rustc_hash::FxHashSet; use std::cmp::Ordering; @@ -117,6 +119,30 @@ impl From for StatementListItem { } } +impl VisitWith for StatementListItem { + fn visit_with<'a>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + StatementListItem::Statement(statement) => visitor.visit_statement(statement), + StatementListItem::Declaration(declaration) => visitor.visit_declaration(declaration), + } + } + + fn visit_with_mut<'a>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + StatementListItem::Statement(statement) => visitor.visit_statement_mut(statement), + StatementListItem::Declaration(declaration) => { + visitor.visit_declaration_mut(declaration) + } + } + } +} + /// List of statements. /// /// More information: @@ -278,3 +304,25 @@ impl ToIndentedString for StatementList { buf } } + +impl VisitWith for StatementList { + fn visit_with<'a>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for statement in self.statements.iter() { + visitor.visit_statement_list_item(statement); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for statement in self.statements.iter_mut() { + visitor.visit_statement_list_item_mut(statement); + } + ControlFlow::Continue(()) + } +} diff --git a/boa_engine/src/syntax/ast/visitor.rs b/boa_engine/src/syntax/ast/visitor.rs new file mode 100644 index 00000000000..b5f1d6a7b2a --- /dev/null +++ b/boa_engine/src/syntax/ast/visitor.rs @@ -0,0 +1,86 @@ +//! Javascript Abstract Syntax Tree visitors. +//! +//! This module contains visitors which can be used to inspect or modify AST nodes. This allows for +//! fine-grained manipulation of ASTs for analysis, rewriting, or instrumentation. + +#[cfg(doc)] +use super::statement_list::StatementListVisitor; + +/// `Try`-like conditional unwrapping of `ControlFlow`. +#[macro_export] +macro_rules! try_break { + ($expr:expr) => { + match $expr { + core::ops::ControlFlow::Continue(c) => c, + core::ops::ControlFlow::Break(b) => return core::ops::ControlFlow::Break(b), + } + }; +} + +use crate::syntax::ast::{Declaration, Statement, StatementList, StatementListItem}; + +macro_rules! define_visit { + ($fn_name:ident, $type_name:ident) => { + fn $fn_name(&mut self, node: &'ast $type_name) -> core::ops::ControlFlow { + node.visit_with(self) + } + }; +} + +macro_rules! define_visit_mut { + ($fn_name:ident, $type_name:ident) => { + fn $fn_name( + &mut self, + node: &'ast mut $type_name, + ) -> core::ops::ControlFlow { + node.visit_with_mut(self) + } + }; +} + +/// Represents an AST visitor. +/// +/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s +/// visitor pattern. +#[allow(unused_variables)] +#[allow(missing_docs)] +pub trait Visitor<'ast>: Sized { + /// Type which will be propagated from the visitor if completing early. + type BreakTy; + + define_visit!(visit_statement_list, StatementList); + define_visit!(visit_statement_list_item, StatementListItem); + define_visit!(visit_statement, Statement); + define_visit!(visit_declaration, Declaration); +} + +/// Represents an AST visitor which can modify AST content. +/// +/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s +/// visitor pattern. +#[allow(unused_variables)] +#[allow(missing_docs)] +pub trait VisitorMut<'ast>: Sized { + /// Type which will be propagated from the visitor if completing early. + type BreakTy; + + define_visit_mut!(visit_statement_list_mut, StatementList); + define_visit_mut!(visit_statement_list_item_mut, StatementListItem); + define_visit_mut!(visit_statement_mut, Statement); + define_visit_mut!(visit_declaration_mut, Declaration); +} + +/// Denotes that a type may be visited, providing a method which allows a visitor to traverse its +/// private fields. +pub trait VisitWith { + /// Visit this node with the provided visitor. + fn visit_with<'a>(&'a self, visitor: &mut V) -> core::ops::ControlFlow + where + V: Visitor<'a>; + + /// Visit this node with the provided visitor mutably, allowing the visitor to modify private + /// fields. + fn visit_with_mut<'a>(&'a mut self, visitor: &mut V) -> core::ops::ControlFlow + where + V: VisitorMut<'a>; +} diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index 3b9ac4fcd3b..680e6b2f309 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -40,8 +40,8 @@ mod r#type; pub use conversions::*; pub use display::ValueDisplay; -pub use equality::*; -pub use hash::*; +// pub use equality::*; +// pub use hash::*; pub use integer::IntegerOrInfinity; pub use operations::*; pub use r#type::Type; diff --git a/boa_examples/src/bin/printer_visitor.rs b/boa_examples/src/bin/printer_visitor.rs new file mode 100644 index 00000000000..33137cf7b23 --- /dev/null +++ b/boa_examples/src/bin/printer_visitor.rs @@ -0,0 +1,64 @@ +use boa_engine::syntax::ast::visitor::{VisitWith, Visitor}; +use boa_engine::syntax::ast::{Declaration, Statement, StatementList, StatementListItem}; +use boa_engine::syntax::Parser; +use boa_engine::Context; +use std::convert::Infallible; +use std::fs::File; +use std::io::BufReader; +use core::ops::ControlFlow; + +#[derive(Debug, Clone, Default)] +struct PrinterVisitor { + indent: String, +} + +impl<'ast> Visitor<'ast> for PrinterVisitor { + type BreakTy = Infallible; + + fn visit_statement_list(&mut self, node: &'ast StatementList) -> ControlFlow { + println!( + "{}StatementList (strict: {}) {{", + self.indent, + node.strict() + ); + self.indent.push(' '); + let res = node.visit_with(self); + self.indent.pop(); + println!("{}}}", self.indent); + res + } + + fn visit_statement_list_item( + &mut self, + node: &'ast StatementListItem, + ) -> ControlFlow { + print!("{}StatementListItem: ", self.indent); + self.indent.push(' '); + let res = node.visit_with(self); + self.indent.pop(); + res + } + + fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow { + println!("Statement: {:?}", node); + ControlFlow::Continue(()) + } + + fn visit_declaration(&mut self, node: &'ast Declaration) -> ControlFlow { + println!("Declaration: {:?}", node); + ControlFlow::Continue(()) + } +} + +fn main() { + let mut parser = Parser::new(BufReader::new( + File::open("boa_examples/scripts/calctest.js").unwrap(), + )); + let mut ctx = Context::default(); + + let statements = parser.parse_all(&mut ctx).unwrap(); + + let mut visitor = PrinterVisitor::default(); + + visitor.visit_statement_list(&statements); +}