Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Implement AST Visitor pattern (attempt #3) #2392

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ad14efe
use chalk/swc patterns
addisoncrump Nov 1, 2022
9b8089a
make fmt happy
addisoncrump Nov 1, 2022
548a44b
whoops, missed a spot
addisoncrump Nov 1, 2022
3905165
more precise missing_docs
addisoncrump Nov 1, 2022
481c433
again
addisoncrump Nov 1, 2022
5250407
Create documentation for Visitor{,Mut} dynamically
addisoncrump Nov 1, 2022
0bbe562
expand for declaration, statement; move V parameter to function
addisoncrump Nov 1, 2022
803ed95
expand for Function
addisoncrump Nov 1, 2022
350656d
expand other function types
addisoncrump Nov 1, 2022
b0b7433
expand class
addisoncrump Nov 1, 2022
fe4bbd5
expand variable
addisoncrump Nov 1, 2022
0b57f6b
expand block
addisoncrump Nov 1, 2022
b2e730e
expand vardeclaration
addisoncrump Nov 1, 2022
4743d45
expand expression
addisoncrump Nov 1, 2022
49e9d15
loops
addisoncrump Nov 1, 2022
0889981
switch
addisoncrump Nov 1, 2022
5c1b507
several additional expansions
addisoncrump Nov 1, 2022
de634a9
formal parameter list
addisoncrump Nov 1, 2022
accf453
class element
addisoncrump Nov 1, 2022
5cccd2e
lots of expansions, forgot to commit individually
addisoncrump Nov 1, 2022
b47bb94
await, yield, loops
addisoncrump Nov 1, 2022
942c38d
several more expansions
addisoncrump Nov 1, 2022
f3a40ef
add todo for formal parameter flag recompute
addisoncrump Nov 1, 2022
89b3d66
implement remaining, prep next pass
addisoncrump Nov 1, 2022
0e06f05
finish visitors
addisoncrump Nov 1, 2022
61ca13d
simplify visitor example
addisoncrump Nov 1, 2022
dc9aa74
Add VisitorMut example
addisoncrump Nov 1, 2022
1dc7657
simplify Cargo.toml
addisoncrump Nov 1, 2022
0953cfc
cleanup examples
addisoncrump Nov 1, 2022
c1ce837
apply clippy fixes
addisoncrump Nov 1, 2022
88c6f01
fixup fmt
addisoncrump Nov 1, 2022
468ecb9
{std=>core}::ops::ControlFlow
addisoncrump Nov 1, 2022
04b2482
undo comment
addisoncrump Nov 1, 2022
58aa397
poof
addisoncrump Nov 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions boa_engine/src/syntax/ast/declaration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -165,3 +167,21 @@ impl ToIndentedString for Declaration {
}
}
}

impl<V> VisitWith<V> for Declaration {
fn visit_with<'a>(&'a self, _visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
// TODO implement
ControlFlow::Continue(())
}

fn visit_with_mut<'a>(&'a mut self, _visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
// TODO implement
ControlFlow::Continue(())
}
}
1 change: 1 addition & 0 deletions boa_engine/src/syntax/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down
20 changes: 20 additions & 0 deletions boa_engine/src/syntax/ast/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -313,3 +315,21 @@ impl ToIndentedString for Statement {
buf
}
}

impl<V> VisitWith<V> for Statement {
fn visit_with<'a>(&'a self, _visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
// TODO implement
ControlFlow::Continue(())
}

fn visit_with_mut<'a>(&'a mut self, _visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
// TODO implement
ControlFlow::Continue(())
}
}
48 changes: 48 additions & 0 deletions boa_engine/src/syntax/ast/statement_list/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -117,6 +119,30 @@ impl From<Declaration> for StatementListItem {
}
}

impl<V> VisitWith<V> for StatementListItem {
fn visit_with<'a>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
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<V::BreakTy>
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:
Expand Down Expand Up @@ -278,3 +304,25 @@ impl ToIndentedString for StatementList {
buf
}
}

impl<V> VisitWith<V> for StatementList {
fn visit_with<'a>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
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<V::BreakTy>
where
V: VisitorMut<'a>,
{
for statement in self.statements.iter_mut() {
visitor.visit_statement_list_item_mut(statement);
}
ControlFlow::Continue(())
}
}
83 changes: 83 additions & 0 deletions boa_engine/src/syntax/ast/visitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! 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.

/// `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) => {
#[allow(missing_docs)]
fn $fn_name(&mut self, node: &'ast $type_name) -> core::ops::ControlFlow<Self::BreakTy> {
node.visit_with(self)
}
};
}

macro_rules! define_visit_mut {
($fn_name:ident, $type_name:ident) => {
#[allow(missing_docs)]
fn $fn_name(
&mut self,
node: &'ast mut $type_name,
) -> core::ops::ControlFlow<Self::BreakTy> {
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)]
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)]
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<V> {
/// Visit this node with the provided visitor.
fn visit_with<'a>(&'a self, visitor: &mut V) -> core::ops::ControlFlow<V::BreakTy>
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<V::BreakTy>
where
V: VisitorMut<'a>;
}
jedel1043 marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 2 additions & 2 deletions boa_engine/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was causing a build failure for me locally...

error: unreachable `pub` item
  --> boa_engine/src/value/mod.rs:43:9
   |
43 | pub use equality::*;
   | ---     ^^^^^^^^
   | |
   | help: consider restricting its visibility: `pub(crate)`
   |
   = help: or consider exporting it for use by other crates
note: the lint level is defined here
  --> boa_engine/src/lib.rs:41:5
   |
41 |     unreachable_pub,
   |     ^^^^^^^^^^^^^^^

Didn't mean to commit it, but easy enough to revert.

Copy link
Member

@jedel1043 jedel1043 Nov 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this didn't break any APIs, I think you can remove it

pub use integer::IntegerOrInfinity;
pub use operations::*;
pub use r#type::Type;
Expand Down
64 changes: 64 additions & 0 deletions boa_examples/src/bin/printer_visitor.rs
Original file line number Diff line number Diff line change
@@ -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 core::ops::ControlFlow;
use std::convert::Infallible;
use std::fs::File;
use std::io::BufReader;

#[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<Self::BreakTy> {
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<Self::BreakTy> {
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<Self::BreakTy> {
println!("Statement: {:?}", node);
ControlFlow::Continue(())
}

fn visit_declaration(&mut self, node: &'ast Declaration) -> ControlFlow<Self::BreakTy> {
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);
}