Factor out generate loops
diff --git a/codegen/src/fold.rs b/codegen/src/fold.rs
index 3bc0036..adcde21 100644
--- a/codegen/src/fold.rs
+++ b/codegen/src/fold.rs
@@ -1,10 +1,11 @@
-use crate::{file, full};
+use crate::{file, full, gen};
 use quote::quote;
 use syn_codegen as types;
 
 const FOLD_SRC: &str = "../src/gen/fold.rs";
 
 mod codegen {
+    use crate::gen;
     use inflections::Inflect;
     use proc_macro2::{Span, TokenStream};
     use quote::{quote, TokenStreamExt};
@@ -166,7 +167,7 @@
                     },
                 )
             }
-            types::Type::Ext(t) if super::TERMINAL_TYPES.contains(&&t[..]) => {
+            types::Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => {
                 Some(simple_visit(t, name))
             }
             types::Type::Ext(_) | types::Type::Std(_) => None,
@@ -288,7 +289,7 @@
         }
 
         let include_fold_impl = match &s.data {
-            types::Data::Private => super::TERMINAL_TYPES.contains(&s.ident.as_str()),
+            types::Data::Private => gen::TERMINAL_TYPES.contains(&s.ident.as_str()),
             types::Data::Struct(_) | types::Data::Enum(_) => true,
         };
 
@@ -312,22 +313,8 @@
     }
 }
 
-const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
-
 pub fn generate(defs: &types::Definitions) {
-    let mut state = codegen::State::default();
-    for s in &defs.types {
-        codegen::generate(&mut state, s, defs);
-    }
-    for tt in TERMINAL_TYPES {
-        let s = types::Node {
-            ident: tt.to_string(),
-            features: types::Features::default(),
-            data: types::Data::Private,
-        };
-        codegen::generate(&mut state, &s, defs);
-    }
-
+    let state = gen::traverse(defs, codegen::generate);
     let full_macro = full::get_macro();
     let fold_trait = state.fold_trait;
     let fold_impl = state.fold_impl;
diff --git a/codegen/src/gen.rs b/codegen/src/gen.rs
new file mode 100644
index 0000000..c0bd4d1
--- /dev/null
+++ b/codegen/src/gen.rs
@@ -0,0 +1,23 @@
+use syn_codegen as types;
+
+pub const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
+
+pub fn traverse<S, F>(defs: &types::Definitions, generate: F) -> S
+where
+    S: Default,
+    F: Fn(&mut S, &types::Node, &types::Definitions),
+{
+    let mut state = S::default();
+    for s in &defs.types {
+        generate(&mut state, s, defs);
+    }
+    for tt in TERMINAL_TYPES {
+        let s = types::Node {
+            ident: tt.to_string(),
+            features: types::Features::default(),
+            data: types::Data::Private,
+        };
+        generate(&mut state, &s, defs);
+    }
+    state
+}
diff --git a/codegen/src/main.rs b/codegen/src/main.rs
index d80782b..c128e06 100644
--- a/codegen/src/main.rs
+++ b/codegen/src/main.rs
@@ -15,6 +15,7 @@
 mod file;
 mod fold;
 mod full;
+mod gen;
 mod json;
 mod parse;
 mod version;
diff --git a/codegen/src/visit.rs b/codegen/src/visit.rs
index b16130e..4679c9f 100644
--- a/codegen/src/visit.rs
+++ b/codegen/src/visit.rs
@@ -1,10 +1,11 @@
-use crate::{file, full};
+use crate::{file, full, gen};
 use quote::quote;
 use syn_codegen as types;
 
 const VISIT_SRC: &str = "../src/gen/visit.rs";
 
 mod codegen {
+    use crate::gen;
     use inflections::Inflect;
     use proc_macro2::{Span, TokenStream};
     use quote::{quote, TokenStreamExt};
@@ -210,7 +211,7 @@
                     },
                 )
             }
-            types::Type::Ext(t) if super::TERMINAL_TYPES.contains(&&t[..]) => {
+            types::Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => {
                 Some(simple_visit(t, name))
             }
             types::Type::Ext(_) | types::Type::Std(_) => None,
@@ -313,22 +314,8 @@
     }
 }
 
-const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
-
 pub fn generate(defs: &types::Definitions) {
-    let mut state = codegen::State::default();
-    for s in &defs.types {
-        codegen::generate(&mut state, s, defs);
-    }
-    for tt in TERMINAL_TYPES {
-        let s = types::Node {
-            ident: tt.to_string(),
-            features: types::Features::default(),
-            data: types::Data::Private,
-        };
-        codegen::generate(&mut state, &s, defs);
-    }
-
+    let state = gen::traverse(defs, codegen::generate);
     let full_macro = full::get_macro();
     let visit_trait = state.visit_trait;
     let visit_impl = state.visit_impl;
diff --git a/codegen/src/visit_mut.rs b/codegen/src/visit_mut.rs
index 9ac6ebf..f32d1c9 100644
--- a/codegen/src/visit_mut.rs
+++ b/codegen/src/visit_mut.rs
@@ -1,10 +1,11 @@
-use crate::{file, full};
+use crate::{file, full, gen};
 use quote::quote;
 use syn_codegen as types;
 
 const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
 
 mod codegen {
+    use crate::gen;
     use inflections::Inflect;
     use proc_macro2::{Span, TokenStream};
     use quote::{quote, TokenStreamExt};
@@ -210,7 +211,7 @@
                     },
                 )
             }
-            types::Type::Ext(t) if super::TERMINAL_TYPES.contains(&&t[..]) => {
+            types::Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => {
                 Some(simple_visit(t, name))
             }
             types::Type::Ext(_) | types::Type::Std(_) => None,
@@ -313,22 +314,8 @@
     }
 }
 
-const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
-
 pub fn generate(defs: &types::Definitions) {
-    let mut state = codegen::State::default();
-    for s in &defs.types {
-        codegen::generate(&mut state, s, defs);
-    }
-    for tt in TERMINAL_TYPES {
-        let s = types::Node {
-            ident: tt.to_string(),
-            features: types::Features::default(),
-            data: types::Data::Private,
-        };
-        codegen::generate(&mut state, &s, defs);
-    }
-
+    let state = gen::traverse(defs, codegen::generate);
     let full_macro = full::get_macro();
     let visit_mut_trait = state.visit_mut_trait;
     let visit_mut_impl = state.visit_mut_impl;