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:
Paul Tagliamonte
2024-06-26 11:53:45 -04:00
committed by GitHub
parent 053bdffc45
commit 68cdb68231

View File

@ -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();
}
}