Implement stopping a walk from the Walk function (#2818)
When I originally wrote the walk stuff, I wanted to be able to stop a traversal by returning false. That didn't get implemented in the first rev, so this will actually build that out so returning false will stop the walk. Signed-off-by: Paul R. Tagliamonte <paul@kittycad.io>
This commit is contained in:
@ -26,190 +26,209 @@ where
|
||||
}
|
||||
|
||||
/// Run the Walker against all [Node]s in a [Program].
|
||||
pub fn walk<'a, WalkT>(prog: &'a Program, f: &WalkT) -> Result<()>
|
||||
pub fn walk<'a, WalkT>(prog: &'a Program, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
f.walk(prog.into())?;
|
||||
if !f.walk(prog.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
for bi in &prog.body {
|
||||
walk_body_item(bi, f)?;
|
||||
if !walk_body_item(bi, f)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn walk_variable_declarator<'a, WalkT>(node: &'a VariableDeclarator, f: &WalkT) -> Result<()>
|
||||
fn walk_variable_declarator<'a, WalkT>(node: &'a VariableDeclarator, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
f.walk(node.into())?;
|
||||
f.walk((&node.id).into())?;
|
||||
walk_value(&node.init, f)?;
|
||||
Ok(())
|
||||
if !f.walk(node.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
if !f.walk((&node.id).into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
walk_value(&node.init, f)
|
||||
}
|
||||
|
||||
fn walk_parameter<'a, WalkT>(node: &'a Parameter, f: &WalkT) -> Result<()>
|
||||
fn walk_parameter<'a, WalkT>(node: &'a Parameter, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
f.walk(node.into())?;
|
||||
f.walk((&node.identifier).into())?;
|
||||
Ok(())
|
||||
if !f.walk(node.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
f.walk((&node.identifier).into())
|
||||
}
|
||||
|
||||
fn walk_member_object<'a, WalkT>(node: &'a MemberObject, f: &WalkT) -> Result<()>
|
||||
fn walk_member_object<'a, WalkT>(node: &'a MemberObject, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
f.walk(node.into())?;
|
||||
Ok(())
|
||||
f.walk(node.into())
|
||||
}
|
||||
|
||||
fn walk_literal_identifier<'a, WalkT>(node: &'a LiteralIdentifier, f: &WalkT) -> Result<()>
|
||||
fn walk_literal_identifier<'a, WalkT>(node: &'a LiteralIdentifier, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
f.walk(node.into())?;
|
||||
Ok(())
|
||||
f.walk(node.into())
|
||||
}
|
||||
|
||||
fn walk_member_expression<'a, WalkT>(node: &'a MemberExpression, f: &WalkT) -> Result<()>
|
||||
fn walk_member_expression<'a, WalkT>(node: &'a MemberExpression, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
f.walk(node.into())?;
|
||||
if !f.walk(node.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
walk_member_object(&node.object, f)?;
|
||||
walk_literal_identifier(&node.property, f)?;
|
||||
if !walk_member_object(&node.object, f)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
walk_literal_identifier(&node.property, f)
|
||||
}
|
||||
|
||||
fn walk_binary_part<'a, WalkT>(node: &'a BinaryPart, f: &WalkT) -> Result<()>
|
||||
fn walk_binary_part<'a, WalkT>(node: &'a BinaryPart, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
match node {
|
||||
BinaryPart::Literal(lit) => f.walk(lit.as_ref().into())?,
|
||||
BinaryPart::Identifier(id) => f.walk(id.as_ref().into())?,
|
||||
BinaryPart::BinaryExpression(be) => f.walk(be.as_ref().into())?,
|
||||
BinaryPart::CallExpression(ce) => f.walk(ce.as_ref().into())?,
|
||||
BinaryPart::UnaryExpression(ue) => {
|
||||
walk_unary_expression(ue, f)?;
|
||||
true
|
||||
}
|
||||
BinaryPart::MemberExpression(me) => {
|
||||
walk_member_expression(me, f)?;
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
BinaryPart::Literal(lit) => f.walk(lit.as_ref().into()),
|
||||
BinaryPart::Identifier(id) => f.walk(id.as_ref().into()),
|
||||
BinaryPart::BinaryExpression(be) => f.walk(be.as_ref().into()),
|
||||
BinaryPart::CallExpression(ce) => f.walk(ce.as_ref().into()),
|
||||
BinaryPart::UnaryExpression(ue) => walk_unary_expression(ue, f),
|
||||
BinaryPart::MemberExpression(me) => walk_member_expression(me, f),
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_value<'a, WalkT>(node: &'a Value, f: &WalkT) -> Result<()>
|
||||
fn walk_value<'a, WalkT>(node: &'a Value, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
match node {
|
||||
Value::Literal(lit) => {
|
||||
f.walk(lit.as_ref().into())?;
|
||||
}
|
||||
Value::TagDeclarator(tag) => {
|
||||
f.walk(tag.as_ref().into())?;
|
||||
}
|
||||
Value::Literal(lit) => f.walk(lit.as_ref().into()),
|
||||
Value::TagDeclarator(tag) => f.walk(tag.as_ref().into()),
|
||||
|
||||
Value::Identifier(id) => {
|
||||
// sometimes there's a bare Identifier without a Value::Identifier.
|
||||
f.walk(id.as_ref().into())?;
|
||||
f.walk(id.as_ref().into())
|
||||
}
|
||||
|
||||
Value::BinaryExpression(be) => {
|
||||
f.walk(be.as_ref().into())?;
|
||||
|
||||
walk_binary_part(&be.left, f)?;
|
||||
walk_binary_part(&be.right, f)?;
|
||||
if !f.walk(be.as_ref().into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
if !walk_binary_part(&be.left, f)? {
|
||||
return Ok(false);
|
||||
}
|
||||
walk_binary_part(&be.right, f)
|
||||
}
|
||||
Value::FunctionExpression(fe) => {
|
||||
f.walk(fe.as_ref().into())?;
|
||||
if !f.walk(fe.as_ref().into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
for arg in &fe.params {
|
||||
walk_parameter(arg, f)?;
|
||||
if !walk_parameter(arg, f)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
walk(&fe.body, f)?;
|
||||
walk(&fe.body, f)
|
||||
}
|
||||
Value::CallExpression(ce) => {
|
||||
f.walk(ce.as_ref().into())?;
|
||||
f.walk((&ce.callee).into())?;
|
||||
for e in &ce.arguments {
|
||||
walk_value::<WalkT>(e, f)?;
|
||||
if !f.walk(ce.as_ref().into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !f.walk((&ce.callee).into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
for e in &ce.arguments {
|
||||
if !walk_value::<WalkT>(e, f)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
Value::PipeExpression(pe) => {
|
||||
f.walk(pe.as_ref().into())?;
|
||||
if !f.walk(pe.as_ref().into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
for e in &pe.body {
|
||||
walk_value::<WalkT>(e, f)?;
|
||||
if !walk_value::<WalkT>(e, f)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
Value::PipeSubstitution(ps) => {
|
||||
f.walk(ps.as_ref().into())?;
|
||||
}
|
||||
Value::PipeSubstitution(ps) => f.walk(ps.as_ref().into()),
|
||||
Value::ArrayExpression(ae) => {
|
||||
f.walk(ae.as_ref().into())?;
|
||||
for e in &ae.elements {
|
||||
walk_value::<WalkT>(e, f)?;
|
||||
if !f.walk(ae.as_ref().into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
for e in &ae.elements {
|
||||
if !walk_value::<WalkT>(e, f)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
Value::ObjectExpression(oe) => {
|
||||
walk_object_expression(oe, f)?;
|
||||
}
|
||||
Value::MemberExpression(me) => {
|
||||
walk_member_expression(me, f)?;
|
||||
}
|
||||
Value::UnaryExpression(ue) => {
|
||||
walk_unary_expression(ue, f)?;
|
||||
}
|
||||
Value::None(_) => {}
|
||||
Value::ObjectExpression(oe) => walk_object_expression(oe, f),
|
||||
Value::MemberExpression(me) => walk_member_expression(me, f),
|
||||
Value::UnaryExpression(ue) => walk_unary_expression(ue, f),
|
||||
Value::None(_) => Ok(true),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Walk through an [ObjectProperty].
|
||||
fn walk_object_property<'a, WalkT>(node: &'a ObjectProperty, f: &WalkT) -> Result<()>
|
||||
fn walk_object_property<'a, WalkT>(node: &'a ObjectProperty, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
f.walk(node.into())?;
|
||||
walk_value(&node.value, f)?;
|
||||
Ok(())
|
||||
if !f.walk(node.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
walk_value(&node.value, f)
|
||||
}
|
||||
|
||||
/// Walk through an [ObjectExpression].
|
||||
fn walk_object_expression<'a, WalkT>(node: &'a ObjectExpression, f: &WalkT) -> Result<()>
|
||||
fn walk_object_expression<'a, WalkT>(node: &'a ObjectExpression, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
f.walk(node.into())?;
|
||||
for prop in &node.properties {
|
||||
walk_object_property(prop, f)?;
|
||||
if !f.walk(node.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
for prop in &node.properties {
|
||||
if !walk_object_property(prop, f)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// walk through an [UnaryExpression].
|
||||
fn walk_unary_expression<'a, WalkT>(node: &'a UnaryExpression, f: &WalkT) -> Result<()>
|
||||
fn walk_unary_expression<'a, WalkT>(node: &'a UnaryExpression, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
f.walk(node.into())?;
|
||||
walk_binary_part(&node.argument, f)?;
|
||||
Ok(())
|
||||
if !f.walk(node.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
walk_binary_part(&node.argument, f)
|
||||
}
|
||||
|
||||
/// walk through a [BodyItem].
|
||||
fn walk_body_item<'a, WalkT>(node: &'a BodyItem, f: &WalkT) -> Result<()>
|
||||
fn walk_body_item<'a, WalkT>(node: &'a BodyItem, f: &WalkT) -> Result<bool>
|
||||
where
|
||||
WalkT: Walker<'a>,
|
||||
{
|
||||
@ -217,20 +236,61 @@ where
|
||||
|
||||
match node {
|
||||
BodyItem::ExpressionStatement(xs) => {
|
||||
f.walk(xs.into())?;
|
||||
walk_value(&xs.expression, f)?;
|
||||
if !f.walk(xs.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
walk_value(&xs.expression, f)
|
||||
}
|
||||
BodyItem::VariableDeclaration(vd) => {
|
||||
f.walk(vd.into())?;
|
||||
for dec in &vd.declarations {
|
||||
walk_variable_declarator(dec, f)?;
|
||||
if !f.walk(vd.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
for dec in &vd.declarations {
|
||||
if !walk_variable_declarator(dec, f)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
BodyItem::ReturnStatement(rs) => {
|
||||
f.walk(rs.into())?;
|
||||
walk_value(&rs.argument, f)?;
|
||||
if !f.walk(rs.into())? {
|
||||
return Ok(false);
|
||||
}
|
||||
walk_value(&rs.argument, f)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! kcl {
|
||||
( $kcl:expr ) => {{
|
||||
let tokens = $crate::token::lexer($kcl).unwrap();
|
||||
let parser = $crate::parser::Parser::new(tokens);
|
||||
parser.ast().unwrap()
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stop_walking() {
|
||||
let program = kcl!(
|
||||
"
|
||||
const foo = 1
|
||||
const bar = 2
|
||||
"
|
||||
);
|
||||
|
||||
walk(&program, &|node| {
|
||||
if let Node::VariableDeclarator(vd) = node {
|
||||
if vd.id.name == "foo" {
|
||||
return Ok(false);
|
||||
}
|
||||
panic!("walk didn't stop");
|
||||
}
|
||||
Ok(true)
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user