Repository: uber/piranha (language: python)

Issue:
Allow user to set `number_of_ancestors` for `Parent` scope



Discussion / hints:

Fix patch:
diff --git a/polyglot/piranha/src/config.rs b/polyglot/piranha/src/config.rs
deleted file mode 100644
index 3618754cd..000000000
--- a/polyglot/piranha/src/config.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-Copyright (c) 2022 Uber Technologies, Inc.
-
- <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- except in compliance with the License. You may obtain a copy of the License at
- <p>http://www.apache.org/licenses/LICENSE-2.0
-
- <p>Unless required by applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- express or implied. See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-//! This module contains all the `structs` and implementations required for - (i) handling Piranha's run-time arguments,
-//! (ii) reading language specific configurations, and (iii) API specific configurations.
-//! This module defines all basic building block `structs` used by Piranha.
-
-use crate::{
-  models::piranha_arguments::PiranhaArguments,
-  models::{
-    outgoing_edges::{Edges, OutgoingEdges},
-    rule::{Rule, Rules},
-    scopes::{ScopeConfig, ScopeGenerator},
-  },
-  utilities::{parse_toml, read_toml},
-};
-
-use std::path::Path;
-
-use clap::Parser;
-
-/// A refactoring tool that eliminates dead code related to stale feature flags.
-#[derive(Clone, Parser, Debug)]
-#[clap(name = "Piranha")]
-pub(crate) struct CommandLineArguments {
-  /// Path to source code folder
-  #[clap(short = 'c', long)]
-  pub(crate) path_to_codebase: String,
-  /// Directory containing the configuration files - `piranha_arguments.toml`, `rules.toml`,  and  `edges.toml` (optional)
-  #[clap(short = 'f', long)]
-  pub(crate) path_to_configurations: String,
-  /// Path to output summary json
-  #[clap(short = 'j', long)]
-  pub(crate) path_to_output_summary: Option<String>,
-  /// Disables in-place rewriting of code
-  #[clap(short = 'd', long, parse(try_from_str), default_value_t = false)]
-  pub(crate) dry_run: bool,
-}
-
-fn read_language_specific_rules(language_name: &str) -> Rules {
-  match language_name {
-    "java" => parse_toml(include_str!("cleanup_rules/java/rules.toml")),
-    "kt" => parse_toml(include_str!("cleanup_rules/kt/rules.toml")),
-    "swift" => parse_toml(include_str!("cleanup_rules/kt/rules.toml")),
-    _ => Rules::default(),
-  }
-}
-
-fn read_language_specific_edges(language_name: &str) -> Edges {
-  match language_name {
-    "java" => parse_toml(include_str!("cleanup_rules/java/edges.toml")),
-    "kt" => parse_toml(include_str!("cleanup_rules/kt/edges.toml")),
-    _ => Edges::default(),
-  }
-}
-
-fn read_scope_config(language_name: &str) -> Vec<ScopeGenerator> {
-  match language_name {
-    "java" => parse_toml::<ScopeConfig>(include_str!("cleanup_rules/java/scope_config.toml"))
-      .scopes()
-      .to_vec(),
-    "kt" => parse_toml::<ScopeConfig>(include_str!("cleanup_rules/kt/scope_config.toml"))
-      .scopes()
-      .to_vec(),
-    "swift" => parse_toml::<ScopeConfig>(include_str!("cleanup_rules/swift/scope_config.toml"))
-      .scopes()
-      .to_vec(),
-    _ => Vec::new(),
-  }
-}
-
-pub(crate) fn read_config_files(
-  args: &PiranhaArguments,
-) -> (Vec<Rule>, Vec<OutgoingEdges>, Vec<ScopeGenerator>) {
-  let path_to_config = Path::new(args.path_to_configurations());
-  // Read the language specific cleanup rules and edges
-  let language_rules: Rules = read_language_specific_rules(args.language_name());
-  let language_edges: Edges = read_language_specific_edges(args.language_name());
-  let scopes = read_scope_config(args.language_name());
-
-  // Read the API specific cleanup rules and edges
-  let mut input_rules: Rules = read_toml(&path_to_config.join("rules.toml"), true);
-  let input_edges: Edges = read_toml(&path_to_config.join("edges.toml"), true);
-
-  for r in input_rules.rules.iter_mut() {
-    r.add_to_seed_rules_group();
-  }
-
-  let all_rules = [language_rules.rules, input_rules.rules].concat();
-  let all_edges = [language_edges.edges, input_edges.edges].concat();
-
-  (all_rules, all_edges, scopes)
-}
diff --git a/polyglot/piranha/src/lib.rs b/polyglot/piranha/src/lib.rs
index e3f40cba9..aa15316f2 100644
--- a/polyglot/piranha/src/lib.rs
+++ b/polyglot/piranha/src/lib.rs
@@ -11,13 +11,11 @@ Copyright (c) 2022 Uber Technologies, Inc.
  limitations under the License.
 */
 
-use config::CommandLineArguments;
 use models::{
   piranha_arguments::PiranhaArguments, piranha_output::PiranhaOutputSummary,
   source_code_unit::SourceCodeUnit,
 };
 
-mod config;
 pub mod models;
 #[cfg(test)]
 mod tests;
@@ -32,7 +30,10 @@ use log::{debug, info};
 use regex::Regex;
 use tree_sitter::Parser;
 
-use crate::{models::rule_store::RuleStore, utilities::read_file};
+use crate::{
+  models::{rule_store::RuleStore, piranha_input::PiranhaInput},
+  utilities::read_file,
+};
 
 use pyo3::prelude::{pyfunction, pymodule, wrap_pyfunction, PyModule, PyResult, Python};
 
@@ -49,13 +50,13 @@ use pyo3::prelude::{pyfunction, pymodule, wrap_pyfunction, PyModule, PyResult, P
 pub fn run_piranha_cli(
   path_to_codebase: String, path_to_configurations: String, dry_run: bool,
 ) -> Vec<PiranhaOutputSummary> {
-  let configuration = PiranhaArguments::new(CommandLineArguments {
+  let args = PiranhaArguments::from(PiranhaInput::API {
     path_to_codebase,
     path_to_configurations,
-    path_to_output_summary: None,
     dry_run,
   });
-  execute_piranha(&configuration)
+  debug!("{:?}", args);
+  execute_piranha(&args)
 }
 
 #[pymodule]
@@ -131,12 +132,12 @@ impl FlagCleaner {
     // Setup the parser for the specific language
     let mut parser = Parser::new();
     parser
-      .set_language(self.rule_store.language())
+      .set_language(*self.rule_store.language())
       .expect("Could not set the language for the parser.");
 
     // Keep looping until new `global` rules are added.
     loop {
-      let current_rules = self.rule_store.global_rules();
+      let current_rules = self.rule_store.global_rules().clone();
 
       debug!("\n # Global rules {}", current_rules.len());
       // Iterate over each file containing the usage of the feature flag API
@@ -193,7 +194,7 @@ impl FlagCleaner {
           .extension()
           .and_then(|e| {
             e.to_str()
-              .filter(|x| x.eq(&self.rule_store.language_name()))
+              .filter(|x| x.eq(&self.rule_store.piranha_args().get_language()))
           })
           .is_some()
       })
diff --git a/polyglot/piranha/src/main.rs b/polyglot/piranha/src/main.rs
index fb2d60e06..5d58b305b 100644
--- a/polyglot/piranha/src/main.rs
+++ b/polyglot/piranha/src/main.rs
@@ -16,7 +16,8 @@ use std::{fs, time::Instant};
 
 use log::{debug, info};
 use polyglot_piranha::{
-  execute_piranha, models::piranha_arguments::PiranhaArguments,
+  execute_piranha,
+  models::{piranha_arguments::PiranhaArguments, piranha_input::PiranhaInput},
   models::piranha_output::PiranhaOutputSummary,
 };
 
@@ -26,7 +27,7 @@ fn main() {
 
   info!("Executing Polyglot Piranha");
 
-  let args = PiranhaArguments::from_command_line();
+  let args = PiranhaArguments::from(PiranhaInput::CommandLineInput);
 
   debug!("Piranha Arguments are \n{:#?}", args);
   let piranha_output_summaries = execute_piranha(&args);
diff --git a/polyglot/piranha/src/models/constraint.rs b/polyglot/piranha/src/models/constraint.rs
index cab4997a1..86425e787 100644
--- a/polyglot/piranha/src/models/constraint.rs
+++ b/polyglot/piranha/src/models/constraint.rs
@@ -11,81 +11,19 @@ Copyright (c) 2022 Uber Technologies, Inc.
  limitations under the License.
 */
 
-use std::collections::HashMap;
-
+use getset::Getters;
 use serde_derive::Deserialize;
-use tree_sitter::Node;
-
-use crate::utilities::tree_sitter_utilities::{
-  get_node_for_range, substitute_tags, PiranhaHelpers,
-};
-
-use super::{rule_store::RuleStore, source_code_unit::SourceCodeUnit};
 
-#[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
+#[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, Getters)]
 pub(crate) struct Constraint {
   /// Scope in which the constraint query has to be applied
+  #[get = "pub"]
   matcher: String,
   /// The Tree-sitter queries that need to be applied in the `matcher` scope
+  #[get = "pub"]
   queries: Vec<String>,
 }
 
-impl Constraint {
-  pub(crate) fn queries(&self) -> &[String] {
-    &self.queries
-  }
-
-  pub(crate) fn matcher(&self, substitutions: &HashMap<String, String>) -> String {
-    substitute_tags(String::from(&self.matcher), substitutions, true)
-  }
-
-  /// Checks if the node satisfies the constraints.
-  /// Constraint has two parts (i) `constraint.matcher` (ii) `constraint.query`.
-  /// This function traverses the ancestors of the given `node` until `constraint.matcher` matches
-  /// i.e. finds scope for constraint.
-  /// Within this scope it checks if the `constraint.query` DOES NOT MATCH any sub-tree.
-  pub(crate) fn is_satisfied(
-    &self, node: Node, source_code_unit: SourceCodeUnit, rule_store: &mut RuleStore,
-    substitutions: &HashMap<String, String>,
-  ) -> bool {
-    let mut current_node = node;
-    // This ensures that the below while loop considers the current node too when checking for constraints.
-    // It does not make sense to check for constraint if current node is a "leaf" node.
-    if node.child_count() > 0 {
-      current_node = node.child(0).unwrap();
-    }
-    // Get the scope_node of the constraint (`scope.matcher`)
-    let mut matched_matcher = false;
-    while let Some(parent) = current_node.parent() {
-      let query_str = &self.matcher(substitutions);
-      if let Some(p_match) =
-        parent.get_match_for_query(&source_code_unit.code(), rule_store.query(query_str), false)
-      {
-        matched_matcher = true;
-        let scope_node = get_node_for_range(
-          source_code_unit.root_node(),
-          p_match.range().start_byte,
-          p_match.range().end_byte,
-        );
-        for query_with_holes in self.queries() {
-          let query_str = substitute_tags(query_with_holes.to_string(), substitutions, true);
-          let query = &rule_store.query(&query_str);
-          // If this query matches anywhere within the scope, return false.
-          if scope_node
-            .get_match_for_query(&source_code_unit.code(), query, true)
-            .is_some()
-          {
-            return false;
-          }
-        }
-        break;
-      }
-      current_node = parent;
-    }
-    matched_matcher
-  }
-}
-
 impl Constraint {
   #[cfg(test)]
   pub(crate) fn new(matcher: String, queries: Vec<String>) -> Self {
diff --git a/polyglot/piranha/src/models/default_configs.rs b/polyglot/piranha/src/models/default_configs.rs
new file mode 100644
index 000000000..764034187
--- /dev/null
+++ b/polyglot/piranha/src/models/default_configs.rs
@@ -0,0 +1,67 @@
+use std::collections::HashMap;
+
+use super::language::PiranhaLanguage;
+
+pub fn default_number_of_ancestors_in_parent_scope() -> u8 {
+  4
+}
+
+pub fn default_languages() -> Vec<String> {
+  vec![default_language()]
+}
+
+pub fn default_language() -> String {
+  "java".to_string()
+}
+
+pub fn default_substitutions() -> Vec<Vec<String>> {
+  vec![]
+}
+
+pub fn default_delete_file_if_empty() -> bool {
+  true
+}
+
+pub fn default_cleanup_comments_buffer() -> usize {
+  2
+}
+
+pub fn default_cleanup_comments() -> bool {
+  false
+}
+
+pub fn default_global_tag_prefix() -> String {
+  "GLOBAL_TAG.".to_string()
+}
+
+pub fn default_dry_run() -> bool {
+  false
+}
+
+pub fn default_name_of_piranha_argument_toml() -> String {
+  "piranha_arguments.toml".to_string()
+}
+
+pub fn default_path_to_code_base() -> String {
+  String::new()
+}
+
+pub fn default_input_substitutions() -> HashMap<String, String> {
+  HashMap::new()
+}
+
+pub fn default_path_to_configurations() -> String {
+  String::new()
+}
+
+pub fn default_path_to_output_summaries() -> Option<String> {
+  None
+}
+
+pub fn default_piranha_language() -> PiranhaLanguage {
+  PiranhaLanguage::default()
+}
+
+pub fn default_delete_consecutive_new_lines() -> bool {
+  false
+}
diff --git a/polyglot/piranha/src/models/edit.rs b/polyglot/piranha/src/models/edit.rs
index f55326d8d..4fe412d00 100644
--- a/polyglot/piranha/src/models/edit.rs
+++ b/polyglot/piranha/src/models/edit.rs
@@ -13,23 +13,27 @@ Copyright (c) 2022 Uber Technologies, Inc.
 
 use std::collections::HashMap;
 
+use getset::Getters;
 use serde_derive::Serialize;
 use tree_sitter::Range;
 
 use super::matches::Match;
 use pyo3::prelude::pyclass;
 
-#[derive(Serialize, Debug, Clone)]
+#[derive(Serialize, Debug, Clone, Getters)]
 #[pyclass]
 pub(crate) struct Edit {
   // The match representing the target site of the edit
   #[pyo3(get)]
+  #[get = "pub"]
   p_match: Match,
   // The string to replace the substring encompassed by the match
   #[pyo3(get)]
+  #[get = "pub"]
   replacement_string: String,
   // The rule used for creating this match-replace
   #[pyo3(get)]
+  #[get = "pub"]
   matched_rule: String,
 }
 
@@ -41,30 +45,14 @@ impl Edit {
       matched_rule,
     }
   }
+}
 
-  #[cfg(test)]
-  pub(crate) fn from(replacement_range: Range, replacement_string: String) -> Self {
-    Self::new(
-      Match::new(replacement_range, HashMap::new()),
-      replacement_string,
-      String::new(),
-    )
-  }
-
-  /// Get the edit's replacement range.
-  pub(crate) fn replacement_range(&self) -> Range {
-    self.p_match.range()
-  }
-
-  pub(crate) fn replacement_string(&self) -> &str {
-    self.replacement_string.as_ref()
-  }
-
-  pub(crate) fn matched_rule(&self) -> String {
-    self.matched_rule.clone()
-  }
-
-  pub(crate) fn matches(&self) -> &HashMap<String, String> {
-    self.p_match.matches()
-  }
+impl From<Range> for Edit {
+    fn from(replacement_range: Range) -> Self {
+        Self {
+            p_match: Match::new(replacement_range, HashMap::new()),
+            replacement_string: String::new(),
+            matched_rule: String::new(),
+        }
+    }
 }
diff --git a/polyglot/piranha/src/models/language.rs b/polyglot/piranha/src/models/language.rs
new file mode 100644
index 000000000..d5a90fcf1
--- /dev/null
+++ b/polyglot/piranha/src/models/language.rs
@@ -0,0 +1,172 @@
+use getset::Getters;
+use serde_derive::Deserialize;
+use tree_sitter::{Parser, Query};
+
+use crate::utilities::parse_toml;
+
+use super::{
+  default_configs::default_language,
+  outgoing_edges::Edges,
+  rule::Rules,
+  scopes::{ScopeConfig, ScopeGenerator},
+};
+
+#[derive(Debug, Clone, Getters, PartialEq)]
+pub struct PiranhaLanguage {
+  #[get = "pub"]
+  name: String,
+  #[get = "pub"]
+  supported_language: SupportedLanguage,
+  #[get = "pub"]
+  language: tree_sitter::Language,
+  #[get = "pub(crate)"]
+  rules: Option<Rules>,
+  #[get = "pub(crate)"]
+  edges: Option<Edges>,
+  #[get = "pub(crate)"]
+  scopes: Vec<ScopeGenerator>,
+  #[get = "pub"]
+  comment_nodes: Vec<String>,
+}
+
+#[derive(Deserialize, Debug, Clone, PartialEq)]
+pub enum SupportedLanguage {
+  Java,
+  Kotlin,
+  Go,
+  Swift,
+  Strings,
+  Ts,
+  Tsx,
+  Python,
+}
+
+impl Default for SupportedLanguage {
+  fn default() -> Self {
+    SupportedLanguage::Java
+  }
+}
+
+impl PiranhaLanguage {
+  pub fn is_comment(&self, kind: String) -> bool {
+    self.comment_nodes().contains(&kind)
+  }
+
+  pub fn create_query(&self, query_str: String) -> Query {
+    let query = Query::new(self.language, query_str.as_str());
+    if let Ok(q) = query {
+      return q;
+    }
+    panic!(
+      "Could not parse the query : {:?} {:?}",
+      query_str,
+      query.err()
+    );
+  }
+
+  pub fn parser(&self) -> Parser {
+    let mut parser = Parser::new();
+    parser
+      .set_language(self.language)
+      .expect("Could not set the language for the parser.");
+    parser
+  }
+}
+
+impl Default for PiranhaLanguage {
+  fn default() -> Self {
+    let rules: Rules = parse_toml(include_str!("../cleanup_rules/java/rules.toml"));
+    let edges: Edges = parse_toml(include_str!("../cleanup_rules/java/edges.toml"));
+    PiranhaLanguage {
+      name: default_language(),
+      supported_language: SupportedLanguage::default(),
+      language: tree_sitter_java::language(),
+      rules: Some(rules),
+      edges: Some(edges),
+      scopes: parse_toml::<ScopeConfig>(include_str!("../cleanup_rules/java/scope_config.toml"))
+        .scopes()
+        .to_vec(),
+      comment_nodes: vec!["line_comment".to_string(), "block_comment".to_string()],
+    }
+  }
+}
+
+impl From<&str> for PiranhaLanguage {
+  fn from(language: &str) -> Self {
+    match language {
+      "java" => PiranhaLanguage::default(),
+      "go" => PiranhaLanguage {
+        name: language.to_string(),
+        supported_language: SupportedLanguage::Go,
+        language: tree_sitter_go::language(),
+        rules: None,
+        edges: None,
+        scopes: vec![],
+        comment_nodes: vec![],
+      },
+      "kt" => {
+        let rules: Rules = parse_toml(include_str!("../cleanup_rules/kt/rules.toml"));
+        let edges: Edges = parse_toml(include_str!("../cleanup_rules/kt/edges.toml"));
+        PiranhaLanguage {
+          name: language.to_string(),
+          supported_language: SupportedLanguage::Kotlin,
+          language: tree_sitter_kotlin::language(),
+          rules: Some(rules),
+          edges: Some(edges),
+          scopes: parse_toml::<ScopeConfig>(include_str!("../cleanup_rules/kt/scope_config.toml"))
+            .scopes()
+            .to_vec(),
+          comment_nodes: vec!["comment".to_string()],
+        }
+      }
+      "py" => PiranhaLanguage {
+        name: language.to_string(),
+        supported_language: SupportedLanguage::Python,
+        language: tree_sitter_python::language(),
+        rules: None,
+        edges: None,
+        scopes: vec![],
+        comment_nodes: vec![],
+      },
+      "swift" => PiranhaLanguage {
+        name: language.to_string(),
+        supported_language: SupportedLanguage::Swift,
+        language: tree_sitter_swift::language(),
+        scopes: parse_toml::<ScopeConfig>(include_str!("../cleanup_rules/swift/scope_config.toml"))
+          .scopes()
+          .to_vec(),
+        comment_nodes: vec!["comment".to_string(), "multiline_comment".to_string()],
+        rules: None,
+        edges: None,
+      },
+      "strings" => PiranhaLanguage {
+        name: language.to_string(),
+        supported_language: SupportedLanguage::Strings,
+        language: tree_sitter_strings::language(),
+        rules: None,
+        edges: None,
+        scopes: vec![],
+        comment_nodes: vec![],
+      },
+      "ts" => PiranhaLanguage {
+        name: language.to_string(),
+        supported_language: SupportedLanguage::Ts,
+        language: tree_sitter_typescript::language_typescript(),
+        rules: None,
+        edges: None,
+        scopes: vec![],
+        comment_nodes: vec![],
+      },
+      "tsx" => PiranhaLanguage {
+        name: language.to_string(),
+        supported_language: SupportedLanguage::Tsx,
+        language: tree_sitter_typescript::language_tsx(),
+        rules: None,
+        edges: None,
+        scopes: vec![],
+        comment_nodes: vec![],
+      },
+      _ => panic!("Language not supported"),
+    }
+  }
+}
diff --git a/polyglot/piranha/src/models/matches.rs b/polyglot/piranha/src/models/matches.rs
index 2b45af6a0..e7aac7d71 100644
--- a/polyglot/piranha/src/models/matches.rs
+++ b/polyglot/piranha/src/models/matches.rs
@@ -13,10 +13,11 @@ Copyright (c) 2022 Uber Technologies, Inc.
 
 use std::collections::HashMap;
 
+use getset::Getters;
 use pyo3::prelude::pyclass;
 use serde_derive::Serialize;
 
-#[derive(Serialize, Debug, Clone)]
+#[derive(Serialize, Debug, Clone, Getters)]
 #[pyclass]
 pub(crate) struct Match {
   // Range of the entire AST node captured by the match
@@ -24,6 +25,7 @@ pub(crate) struct Match {
   range: Range,
   // The mapping between tags and string representation of the AST captured.
   #[pyo3(get)]
+  #[get = "pub"]
   matches: HashMap<String, String>,
 }
 
@@ -61,10 +63,6 @@ impl Match {
       },
     }
   }
-
-  pub(crate) fn matches(&self) -> &HashMap<String, String> {
-    &self.matches
-  }
 }
 /// A range of positions in a multi-line text document, both in terms of bytes and of
 /// rows and columns.
diff --git a/polyglot/piranha/src/models/mod.rs b/polyglot/piranha/src/models/mod.rs
index 279772fc6..d0032fac8 100644
--- a/polyglot/piranha/src/models/mod.rs
+++ b/polyglot/piranha/src/models/mod.rs
@@ -16,10 +16,12 @@ pub(crate) mod edit;
 pub(crate) mod matches;
 pub(crate) mod outgoing_edges;
 pub mod piranha_arguments;
-pub(crate) mod piranha_config;
+pub mod piranha_input;
 pub mod piranha_output;
 pub(crate) mod rule;
 pub(crate) mod rule_graph;
 pub(crate) mod rule_store;
 pub(crate) mod scopes;
 pub(crate) mod source_code_unit;
+pub(crate) mod default_configs;
+pub(crate) mod language;
diff --git a/polyglot/piranha/src/models/outgoing_edges.rs b/polyglot/piranha/src/models/outgoing_edges.rs
index 2fc85d55f..9bd3b0f7d 100644
--- a/polyglot/piranha/src/models/outgoing_edges.rs
+++ b/polyglot/piranha/src/models/outgoing_edges.rs
@@ -11,6 +11,7 @@ Copyright (c) 2022 Uber Technologies, Inc.
  limitations under the License.
 */
 
+use getset::Getters;
 use serde_derive::Deserialize;
 
 #[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, Default)]
@@ -20,23 +21,12 @@ pub(crate) struct Edges {
 }
 
 // Captures an entry from the `edges.toml` file.
-#[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, Default)]
+#[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, Default, Getters)]
 pub(crate) struct OutgoingEdges {
+  #[get = "pub with_prefix"]
   from: String,
+  #[get = "pub with_prefix"]
   to: Vec<String>,
+  #[get = "pub with_prefix"]
   scope: String,
 }
-
-impl OutgoingEdges {
-  pub(crate) fn source(&self) -> String {
-    String::from(&self.from)
-  }
-
-  pub(crate) fn sinks(&self) -> Vec<String> {
-    self.to.clone()
-  }
-
-  pub(crate) fn scope(&self) -> &str {
-    self.scope.as_ref()
-  }
-}
diff --git a/polyglot/piranha/src/models/piranha_arguments.rs b/polyglot/piranha/src/models/piranha_arguments.rs
index 0e03a8354..497098165 100644
--- a/polyglot/piranha/src/models/piranha_arguments.rs
+++ b/polyglot/piranha/src/models/piranha_arguments.rs
@@ -12,140 +12,220 @@ Copyright (c) 2022 Uber Technologies, Inc.
 */
 
 use clap::Parser;
-use colored::Colorize;
+
+use super::{
+  default_configs::{
+    default_cleanup_comments, default_cleanup_comments_buffer,
+    default_delete_consecutive_new_lines, default_delete_file_if_empty, default_dry_run,
+    default_global_tag_prefix, default_input_substitutions, default_languages,
+    default_number_of_ancestors_in_parent_scope, default_path_to_code_base,
+    default_path_to_configurations, default_path_to_output_summaries, default_piranha_language,
+    default_substitutions,
+  },
+  language::PiranhaLanguage,
+};
+use crate::utilities::read_toml;
 use derive_builder::Builder;
 use getset::{CopyGetters, Getters};
-use log::info;
+use serde_derive::Deserialize;
 use std::{collections::HashMap, path::PathBuf};
-use tree_sitter::Language;
-
-use crate::{
-  config::CommandLineArguments,
-  models::piranha_config::PiranhaConfiguration,
-  utilities::{read_toml, tree_sitter_utilities::TreeSitterHelpers},
-};
 
-#[derive(Clone, Builder, Getters, CopyGetters, Debug)]
-#[builder(default)]
-/// Captures the processed Piranha arguments (Piranha-Configuration) parsed from `path_to_feature_flag_rules`.
+/// A refactoring tool that eliminates dead code related to stale feature flags.
+#[derive(Deserialize, Clone, Builder, Getters, CopyGetters, Debug, Parser, Default)]
+#[clap(name = "Piranha")]
 pub struct PiranhaArguments {
   /// Path to source code folder.
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_path_to_code_base()")]
+  #[clap(short = 'c', long = "path_to_code_base")]
+  #[serde(skip)]
   path_to_code_base: String,
   // Input arguments provided to Piranha, mapped to tag names -
   // @stale_flag_name, @namespace, @treated, @treated_complement
   // These substitutions instantiate the initial set of feature flag rules.
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_input_substitutions()")]
+  #[clap(skip)]
+  #[serde(skip)]
   input_substitutions: HashMap<String, String>,
+
+  #[builder(setter(skip))]
+  #[clap(skip)]
+  #[serde(default = "default_substitutions")]
+  substitutions: Vec<Vec<String>>,
+
   /// Folder containing the API specific rules
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_path_to_configurations()")]
+  #[clap(short = 'f', long = "path_to_configurations")]
+  #[serde(skip)]
   path_to_configurations: String,
-  /// File to which the output summary should be written
-  #[getset(get = "pub")]
+  /// Path to output summary json file
+  #[get = "pub"]
+  #[builder(default = "default_path_to_output_summaries()")]
+  #[clap(short = 'j', long = "path_to_output_summaries")]
+  #[serde(skip)]
   path_to_output_summaries: Option<String>,
-  /// Tree-sitter language model
-  #[getset(get_copy = "pub")]
-  language: Language,
-  // The language name is file the extension used for files in particular language.
-  #[getset(get = "pub")]
-  language_name: String,
+
+  // a list of file extensions
+  // #[get = "pub"]
+  #[builder(default = "default_languages()")]
+  #[clap(skip)]
+  #[serde(default = "default_languages")]
+  language: Vec<String>,
+
+  #[get = "pub"]
+  #[builder(default = "default_piranha_language()")]
+  #[clap(skip)]
+  #[serde(skip)]
+  piranha_language: PiranhaLanguage,
+
   // User option that determines whether an empty file will be deleted
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_delete_file_if_empty()")]
+  #[clap(skip)]
+  #[serde(default = "default_delete_file_if_empty")]
   delete_file_if_empty: bool,
   // User option that determines whether consecutive newline characters will be
   // replaced with a newline character
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_delete_consecutive_new_lines()")]
+  #[clap(skip)]
+  #[serde(default = "default_delete_consecutive_new_lines")]
   delete_consecutive_new_lines: bool,
   // User option that determines the prefix used for tag names that should be considered
   /// global i.e. if a global tag is found when rewriting a source code unit
   /// All source code units from this point will have access to this global tag.
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_global_tag_prefix()")]
+  #[clap(skip)]
+  #[serde(default = "default_global_tag_prefix")]
   global_tag_prefix: String,
   /// Add a user option to configure the number of ancestors considered when applying
   /// parent scoped rules
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_number_of_ancestors_in_parent_scope()")]
+  #[clap(skip)]
+  #[serde(default = "default_number_of_ancestors_in_parent_scope")]
   number_of_ancestors_in_parent_scope: u8,
   /// The number of lines to consider for cleaning up the comments
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_cleanup_comments_buffer()")]
+  #[clap(skip)]
+  #[serde(default = "default_cleanup_comments_buffer")]
   cleanup_comments_buffer: usize,
   /// The AST Kinds for which comments should be deleted
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_cleanup_comments()")]
+  #[clap(skip)]
+  #[serde(default = "default_cleanup_comments")]
   cleanup_comments: bool,
   /// Disables in-place rewriting of code
-  #[getset(get = "pub")]
+  #[get = "pub"]
+  #[builder(default = "default_dry_run()")]
+  #[clap(short = 'd', long, parse(try_from_str), default_value_t = false)]
+  #[serde(default = "default_dry_run")]
   dry_run: bool,
 }
 
 impl PiranhaArguments {
-  pub fn from_command_line() -> Self {
-    Self::new(CommandLineArguments::parse())
+  pub(crate) fn substitutions(&self) -> HashMap<String, String> {
+    self
+      .substitutions
+      .iter()
+      .map(|x| (x[0].clone(), x[1].clone()))
+      .collect()
   }
 
-  pub(crate) fn new(args: CommandLineArguments) -> Self {
-    let path_to_piranha_argument_file =
-      PathBuf::from(args.path_to_configurations.as_str()).join("piranha_arguments.toml");
-
-    let piranha_args_from_config: PiranhaConfiguration =
-      read_toml(&path_to_piranha_argument_file, false);
-
-    let input_substitutions = piranha_args_from_config.substitutions();
-
-    #[rustfmt::skip]
-    info!("{}",  format!("Piranha arguments are :\n {:?}", input_substitutions).purple());
-
-    let mut args_builder = PiranhaArgumentsBuilder::default();
+  pub fn get_language(&self) -> String {
+    self.language[0].clone()
+  }
 
-    args_builder
-      .path_to_code_base(args.path_to_codebase.to_string())
+  pub(crate) fn new(path_to_piranha_arguments_toml: PathBuf) -> Self {
+    let args: PiranhaArguments = read_toml(&path_to_piranha_arguments_toml, false);
+    let input_substitutions = args.substitutions();
+    let piranha_language = PiranhaLanguage::from(args.get_language().as_str());
+    let derived_args = PiranhaArgumentsBuilder::default()
       .input_substitutions(input_substitutions)
-      .path_to_configurations(args.path_to_configurations)
-      .path_to_output_summaries(args.path_to_output_summary)
-      .language_name(piranha_args_from_config.language())
-      .language(piranha_args_from_config.language().get_language())
-      .dry_run(args.dry_run);
-
-    if let Some(v) = piranha_args_from_config.delete_file_if_empty() {
-      args_builder.delete_file_if_empty(v);
-    }
-    if let Some(v) = piranha_args_from_config.delete_consecutive_new_lines() {
-      args_builder.delete_consecutive_new_lines(v);
-    }
-    if let Some(v) = piranha_args_from_config.global_tag_prefix() {
-      args_builder.global_tag_prefix(v);
-    }
-
-    if let Some(buffer_size) = piranha_args_from_config.cleanup_comments_buffer() {
-      args_builder.cleanup_comments_buffer(buffer_size);
-    }
+      .piranha_language(piranha_language)
+      .build()
+      .unwrap();
+    args.merge(derived_args)
+  }
 
-    if let Some(ast_kinds) = piranha_args_from_config.cleanup_comments() {
-      args_builder.cleanup_comments(ast_kinds);
+  // Returns non-default valued item when possible
+  fn _merge<T: Clone + std::cmp::PartialEq>(x: T, y: T, default: T) -> T {
+    if x != default {
+      x.clone()
+    } else {
+      y.clone()
     }
-
-    args_builder.build().unwrap()
   }
-}
 
-impl Default for PiranhaArguments {
-  fn default() -> Self {
-    let language_name = String::from("java");
-    PiranhaArguments {
-      path_to_code_base: String::new(),
-      input_substitutions: HashMap::new(),
-      path_to_configurations: String::new(),
-      path_to_output_summaries: None,
-      language: language_name.get_language(),
-      language_name,
-      delete_consecutive_new_lines: false,
-      delete_file_if_empty: true,
-      /// default Global prefix tag us "GLOBAL_TAG."
-      /// i.e. it expects global tag names to look like
-      /// @GLOBAL_TAG.class_name
-      global_tag_prefix: "GLOBAL_TAG.".to_string(),
-      number_of_ancestors_in_parent_scope: 4,
-      cleanup_comments_buffer: 2,
-      cleanup_comments: false,
-      dry_run: false,
+  pub(crate) fn merge(&self, other: PiranhaArguments) -> Self {
+    Self {
+      path_to_code_base: Self::_merge(
+        self.path_to_code_base.clone(),
+        other.path_to_code_base,
+        default_path_to_code_base(),
+      ),
+      input_substitutions: Self::_merge(
+        self.input_substitutions.clone(),
+        other.input_substitutions,
+        default_input_substitutions(),
+      ),
+      substitutions: Self::_merge(
+        self.substitutions.clone(),
+        other.substitutions,
+        default_substitutions(),
+      ),
+      path_to_configurations: Self::_merge(
+        self.path_to_configurations.clone(),
+        other.path_to_configurations,
+        default_path_to_configurations(),
+      ),
+      path_to_output_summaries: Self::_merge(
+        self.path_to_output_summaries.clone(),
+        other.path_to_output_summaries,
+        default_path_to_output_summaries(),
+      ),
+      language: Self::_merge(self.language.clone(), other.language, default_languages()),
+      piranha_language: Self::_merge(
+        self.piranha_language.clone(),
+        other.piranha_language,
+        default_piranha_language(),
+      ),
+      delete_file_if_empty: Self::_merge(
+        self.delete_file_if_empty,
+        other.delete_file_if_empty,
+        default_delete_file_if_empty(),
+      ),
+      delete_consecutive_new_lines: Self::_merge(
+        self.delete_consecutive_new_lines,
+        other.delete_consecutive_new_lines,
+        default_delete_consecutive_new_lines(),
+      ),
+      global_tag_prefix: Self::_merge(
+        self.global_tag_prefix.clone(),
+        other.global_tag_prefix,
+        default_global_tag_prefix(),
+      ),
+      number_of_ancestors_in_parent_scope: Self::_merge(
+        self.number_of_ancestors_in_parent_scope,
+        other.number_of_ancestors_in_parent_scope,
+        default_number_of_ancestors_in_parent_scope(),
+      ),
+      cleanup_comments_buffer: Self::_merge(
+        self.cleanup_comments_buffer,
+        other.cleanup_comments_buffer,
+        default_cleanup_comments_buffer(),
+      ),
+      cleanup_comments: Self::_merge(
+        self.cleanup_comments,
+        other.cleanup_comments,
+        default_cleanup_comments(),
+      ),
+      dry_run: Self::_merge(self.dry_run, other.dry_run, default_dry_run()),
     }
   }
 }
diff --git a/polyglot/piranha/src/models/piranha_config.rs b/polyglot/piranha/src/models/piranha_config.rs
deleted file mode 100644
index 68d952cc2..000000000
--- a/polyglot/piranha/src/models/piranha_config.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-Copyright (c) 2022 Uber Technologies, Inc.
-
- <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- except in compliance with the License. You may obtain a copy of the License at
- <p>http://www.apache.org/licenses/LICENSE-2.0
-
- <p>Unless required by applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- express or implied. See the License for the specific language governing permissions and
- limitations under the License.
-*/
-use serde_derive::Deserialize;
-use std::collections::HashMap;
-
-/// Captures the Piranha arguments by from the file at `path_to_feature_flag_rules`.
-#[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, Default)]
-pub(crate) struct PiranhaConfiguration {
-  language: Vec<String>,
-  substitutions: Vec<Vec<String>>,
-  delete_file_if_empty: Option<bool>,
-  delete_consecutive_new_lines: Option<bool>,
-  global_tag_prefix: Option<String>,
-  cleanup_comments_buffer: Option<usize>,
-  cleanup_comments: Option<bool>,
-}
-
-impl PiranhaConfiguration {
-  pub(crate) fn substitutions(&self) -> HashMap<String, String> {
-    self
-      .substitutions
-      .iter()
-      .map(|x| (String::from(&x[0]), String::from(&x[1])))
-      .collect()
-  }
-
-  pub(crate) fn language(&self) -> String {
-    self.language[0].clone()
-  }
-
-  pub(crate) fn delete_file_if_empty(&self) -> Option<bool> {
-    self.delete_file_if_empty
-  }
-
-  pub(crate) fn delete_consecutive_new_lines(&self) -> Option<bool> {
-    self.delete_consecutive_new_lines
-  }
-
-  pub(crate) fn global_tag_prefix(&self) -> Option<String> {
-    self.global_tag_prefix.clone()
-  }
-
-  pub(crate) fn cleanup_comments_buffer(&self) -> Option<usize> {
-    self.cleanup_comments_buffer
-  }
-
-  pub(crate) fn cleanup_comments(&self) -> Option<bool> {
-    self.cleanup_comments
-  }
-}
diff --git a/polyglot/piranha/src/models/piranha_input.rs b/polyglot/piranha/src/models/piranha_input.rs
new file mode 100644
index 000000000..a5cc16a05
--- /dev/null
+++ b/polyglot/piranha/src/models/piranha_input.rs
@@ -0,0 +1,40 @@
+use std::path::PathBuf;
+
+use clap::Parser;
+
+use super::{
+  default_configs::default_name_of_piranha_argument_toml,
+  piranha_arguments::{PiranhaArguments, PiranhaArgumentsBuilder},
+};
+
+pub enum PiranhaInput {
+  CommandLineInput,
+  API {
+    path_to_codebase: String,
+    path_to_configurations: String,
+    dry_run: bool,
+  },
+}
+
+impl From<PiranhaInput> for PiranhaArguments {
+  fn from(input: PiranhaInput) -> Self {
+    let input_opts = match input {
+      PiranhaInput::CommandLineInput => PiranhaArguments::parse(),
+      PiranhaInput::API {
+        path_to_codebase,
+        path_to_configurations,
+        dry_run,
+      } => PiranhaArgumentsBuilder::default()
+        .path_to_code_base(path_to_codebase)
+        .path_to_configurations(path_to_configurations)
+        .path_to_output_summaries(None)
+        .dry_run(dry_run)
+        .build()
+        .unwrap(),
+    };
+    let path_to_toml = PathBuf::from(input_opts.path_to_configurations())
+      .join(default_name_of_piranha_argument_toml());
+    let piranha_argument = PiranhaArguments::new(path_to_toml);
+    return input_opts.merge(piranha_argument);
+  }
+}
diff --git a/polyglot/piranha/src/models/rule.rs b/polyglot/piranha/src/models/rule.rs
index 232d6f330..44e8f1bdc 100644
--- a/polyglot/piranha/src/models/rule.rs
+++ b/polyglot/piranha/src/models/rule.rs
@@ -18,18 +18,18 @@ use serde_derive::Deserialize;
 
 use crate::utilities::{tree_sitter_utilities::substitute_tags, MapOfVec};
 
-use super::{constraint::Constraint};
+use super::constraint::Constraint;
 
 static SEED: &str = "Seed Rule";
 static CLEAN_UP: &str = "Cleanup Rule";
 
-#[derive(Deserialize, Debug, Clone, Default)]
+#[derive(Deserialize, Debug, Clone, Default, PartialEq)]
 // Represents the `rules.toml` file
 pub(crate) struct Rules {
   pub(crate) rules: Vec<Rule>,
 }
 
-#[derive(Deserialize, Debug, Clone, Default)]
+#[derive(Deserialize, Debug, Clone, Default, PartialEq)]
 pub(crate) struct Rule {
   /// Name of the rule. (It is unique)
   name: String,
diff --git a/polyglot/piranha/src/models/rule_graph.rs b/polyglot/piranha/src/models/rule_graph.rs
index 65fa8cd52..175254abd 100644
--- a/polyglot/piranha/src/models/rule_graph.rs
+++ b/polyglot/piranha/src/models/rule_graph.rs
@@ -42,13 +42,13 @@ impl RuleGraph {
     // Add the edge(s) to the graph. Multiple edges will be added
     // when either edge endpoint is a group name.
     for edge in edges {
-      for from_rule in get_rules_for_tag_or_name(&edge.source()) {
-        for outgoing_edge in edge.sinks() {
+      for from_rule in get_rules_for_tag_or_name(&edge.get_from()) {
+        for outgoing_edge in edge.get_to() {
           for to_rule in get_rules_for_tag_or_name(&outgoing_edge) {
             // Add edge to the adjacency list
             graph.collect(
               from_rule.clone(),
-              (String::from(edge.scope()), to_rule.clone()),
+              (String::from(edge.get_scope()), to_rule.clone()),
             );
           }
         }
diff --git a/polyglot/piranha/src/models/rule_store.rs b/polyglot/piranha/src/models/rule_store.rs
index 091f2e728..913bcfeeb 100644
--- a/polyglot/piranha/src/models/rule_store.rs
+++ b/polyglot/piranha/src/models/rule_store.rs
@@ -11,27 +11,33 @@ Copyright (c) 2022 Uber Technologies, Inc.
  limitations under the License.
 */
 
-use std::collections::HashMap;
+use std::{collections::HashMap, path::Path};
 
 use colored::Colorize;
+use getset::Getters;
 use log::{debug, info, trace};
 use tree_sitter::{Language, Query};
 
 use crate::{
-  config::read_config_files,
-  models::piranha_arguments::PiranhaArguments,
+  models::piranha_arguments::{PiranhaArguments, PiranhaArgumentsBuilder},
   models::{
     rule::Rule,
     rule_graph::RuleGraph,
     scopes::{ScopeGenerator, ScopeQueryGenerator},
   },
-  utilities::{tree_sitter_utilities::TreeSitterHelpers, MapOfVec},
+  utilities::{read_toml, MapOfVec},
+};
+
+use super::{
+  language::PiranhaLanguage,
+  outgoing_edges::{Edges, OutgoingEdges},
+  rule::Rules,
 };
 
 pub(crate) static GLOBAL: &str = "Global";
 pub(crate) static PARENT: &str = "Parent";
 /// This maintains the state for Piranha.
-#[derive(Debug)]
+#[derive(Debug, Getters)]
 pub(crate) struct RuleStore {
   // A graph that captures the flow amongst the rules
   rule_graph: RuleGraph,
@@ -40,13 +46,19 @@ pub(crate) struct RuleStore {
   // All the input rules stored by name
   rules_by_name: HashMap<String, Rule>,
   // Current global rules to be applied.
+  #[get = "pub"]
   global_rules: Vec<Rule>,
   // Scope generators.
   scopes: Vec<ScopeGenerator>,
   // Command line arguments passed to piranha
+  #[get = "pub"]
   piranha_args: PiranhaArguments,
   // Command line arguments passed to piranha
+  #[get = "pub"]
   global_tags: HashMap<String, String>,
+  /// Tree-sitter language model
+  #[get = "pub"]
+  language: Language,
 }
 
 impl RuleStore {
@@ -55,12 +67,11 @@ impl RuleStore {
     let rule_graph = RuleGraph::new(&edges, &rules);
     let mut rule_store = RuleStore {
       rule_graph,
-      rule_query_cache: HashMap::new(),
       rules_by_name: rules.iter().map(|r| (r.name(), r.clone())).collect(),
-      global_rules: vec![],
       scopes,
       piranha_args: args.clone(),
-      global_tags: HashMap::new(),
+      language: *args.piranha_language().language(),
+      ..Default::default()
     };
 
     for (_, rule) in rule_store.rules_by_name.clone() {
@@ -78,29 +89,12 @@ impl RuleStore {
 
   #[cfg(test)]
   pub(crate) fn default_with_scopes(scopes: Vec<ScopeGenerator>) -> RuleStore {
-    RuleStore{
+    RuleStore {
       scopes,
       ..Default::default()
     }
   }
 
-
-  pub(crate) fn global_rules(&self) -> Vec<Rule> {
-    self.global_rules.clone()
-  }
-
-  pub(crate) fn language(&self) -> Language {
-    self.piranha_args.language()
-  }
-
-  pub(crate) fn language_name(&self) -> &str {
-    self.piranha_args.language_name()
-  }
-
-  pub(crate) fn get_number_of_ancestors_in_parent_scope(&self) -> &u8 {
-    self.piranha_args.number_of_ancestors_in_parent_scope()
-  }
-
   pub(crate) fn default_substitutions(&self) -> HashMap<String, String> {
     let mut default_subs = self.piranha_args.input_substitutions().clone();
     default_subs.extend(self.global_tags().clone());
@@ -126,11 +120,15 @@ impl RuleStore {
   /// Get the compiled query for the `query_str` from the cache
   /// else compile it, add it to the cache and return it.
   pub(crate) fn query(&mut self, query_str: &String) -> &Query {
-    let language = self.language();
     self
       .rule_query_cache
       .entry(query_str.to_string())
-      .or_insert_with(|| query_str.create_query(language))
+      .or_insert_with(|| {
+        self
+          .piranha_args
+          .piranha_language()
+          .create_query(query_str.to_string())
+      })
   }
 
   /// Get the next rules to be applied grouped by the scope in which they should be performed.
@@ -178,10 +176,6 @@ impl RuleStore {
       .unwrap_or_else(Vec::new)
   }
 
-  pub(crate) fn global_tags(&self) -> &HashMap<String, String> {
-    &self.global_tags
-  }
-
   pub(crate) fn add_global_tags(&mut self, new_entries: &HashMap<String, String>) {
     let global_substitutions: HashMap<String, String> = new_entries
       .iter()
@@ -190,23 +184,42 @@ impl RuleStore {
       .collect();
     let _ = &self.global_tags.extend(global_substitutions);
   }
-
-  pub(crate) fn piranha_args(&self) -> &PiranhaArguments {
-    &self.piranha_args
-  }
 }
 
-
-impl Default for RuleStore{
+impl Default for RuleStore {
   fn default() -> Self {
     RuleStore {
       rule_graph: RuleGraph::default(),
-      rule_query_cache: HashMap::new(),
-      rules_by_name: HashMap::new(),
-      global_rules: vec![],
-      piranha_args: PiranhaArguments::default(),
-      scopes: vec![],
-      global_tags: HashMap::new(),
+      rule_query_cache: HashMap::default(),
+      rules_by_name: HashMap::default(),
+      global_rules: Vec::default(),
+      piranha_args: PiranhaArgumentsBuilder::default().build().unwrap(),
+      scopes: Vec::default(),
+      global_tags: HashMap::default(),
+      language: *PiranhaLanguage::default().language(),
     }
   }
 }
+
+fn read_config_files(
+  args: &PiranhaArguments,
+) -> (Vec<Rule>, Vec<OutgoingEdges>, Vec<ScopeGenerator>) {
+  let path_to_config = Path::new(args.path_to_configurations());
+  // Read the language specific cleanup rules and edges
+  let language_rules: Rules = args.piranha_language().rules().clone().unwrap_or_default();
+  let language_edges: Edges = args.piranha_language().edges().clone().unwrap_or_default();
+  let scopes = args.piranha_language().scopes().to_vec();
+
+  // Read the API specific cleanup rules and edges
+  let mut input_rules: Rules = read_toml(&path_to_config.join("rules.toml"), true);
+  let input_edges: Edges = read_toml(&path_to_config.join("edges.toml"), true);
+
+  for r in input_rules.rules.iter_mut() {
+    r.add_to_seed_rules_group();
+  }
+
+  let all_rules = [language_rules.rules, input_rules.rules].concat();
+  let all_edges = [language_edges.edges, input_edges.edges].concat();
+
+  (all_rules, all_edges, scopes)
+}
diff --git a/polyglot/piranha/src/models/scopes.rs b/polyglot/piranha/src/models/scopes.rs
index 3eeb4c079..97291776d 100644
--- a/polyglot/piranha/src/models/scopes.rs
+++ b/polyglot/piranha/src/models/scopes.rs
@@ -13,15 +13,8 @@ Copyright (c) 2022 Uber Technologies, Inc.
 
 use derive_builder::Builder;
 use getset::Getters;
-use log::trace;
 use serde_derive::Deserialize;
 
-use crate::utilities::tree_sitter_utilities::{
-  get_node_for_range, substitute_tags, PiranhaHelpers,
-};
-
-use super::{rule_store::RuleStore, source_code_unit::SourceCodeUnit};
-
 // Represents the content in the `scope_config.toml` file
 #[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, Default, Getters)]
 pub(crate) struct ScopeConfig {
@@ -38,46 +31,6 @@ pub(crate) struct ScopeGenerator {
   rules: Vec<ScopeQueryGenerator>,
 }
 
-impl ScopeGenerator {
-  /// Generate a tree-sitter based query representing the scope of the previous edit.
-  /// We generate these scope queries by matching the rules provided in `<lang>_scopes.toml`.
-  pub(crate) fn get_scope_query(
-    source_code_unit: SourceCodeUnit, scope_level: &str, start_byte: usize, end_byte: usize,
-    rules_store: &mut RuleStore,
-  ) -> String {
-    let root_node = source_code_unit.root_node();
-    let mut changed_node = get_node_for_range(root_node, start_byte, end_byte);
-    // Get the scope matchers for `scope_level` from the `scope_config.toml`.
-    let scope_matchers = rules_store.get_scope_query_generators(scope_level);
-
-    // Match the `scope_matcher.matcher` to the parent
-    loop {
-      trace!(
-        "Getting scope {} for node kind {}",
-        scope_level,
-        changed_node.kind()
-      );
-      for m in &scope_matchers {
-        if let Some(p_match) = changed_node.get_match_for_query(
-          &source_code_unit.code(),
-          rules_store.query(&m.matcher()),
-          false,
-        ) {
-          // Generate the scope query for the specific context by substituting the
-          // the tags with code snippets appropriately in the `generator` query.
-          return substitute_tags(m.generator().to_string(), p_match.matches(), true);
-        }
-      }
-      if let Some(parent) = changed_node.parent() {
-        changed_node = parent;
-      } else {
-        break;
-      }
-    }
-    panic!("Could not create scope query for {:?}", scope_level);
-  }
-}
-
 #[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, Default, Getters, Builder)]
 pub(crate) struct ScopeQueryGenerator {
   #[get = "pub"]
diff --git a/polyglot/piranha/src/models/source_code_unit.rs b/polyglot/piranha/src/models/source_code_unit.rs
index ea8586896..33ad8249a 100644
--- a/polyglot/piranha/src/models/source_code_unit.rs
+++ b/polyglot/piranha/src/models/source_code_unit.rs
@@ -27,13 +27,13 @@ use crate::{
   models::rule_store::{GLOBAL, PARENT},
   utilities::tree_sitter_utilities::{
     get_context, get_node_for_range, get_replace_range, get_tree_sitter_edit, substitute_tags,
-    PiranhaHelpers, TreeSitterHelpers,
+    PiranhaHelpers,
   },
 };
 
 use super::{
-  edit::Edit, matches::Match, piranha_arguments::PiranhaArguments, rule::Rule,
-  rule_store::RuleStore, scopes::ScopeGenerator,
+  constraint::Constraint, edit::Edit, matches::Match, piranha_arguments::PiranhaArguments,
+  rule::Rule, rule_store::RuleStore,
 };
 use getset::{CopyGetters, Getters, MutGetters};
 // Maintains the updated source code content and AST of the file
@@ -134,7 +134,7 @@ impl SourceCodeUnit {
         query_again = true;
 
         // Add all the (code_snippet, tag) mapping to the substitution table.
-        self.add_to_substitutions(edit.matches(), rule_store);
+        self.add_to_substitutions(edit.p_match().matches(), rule_store);
 
         // Apply edit_1
         let applied_ts_edit = self.apply_edit(&edit, parser);
@@ -243,9 +243,9 @@ impl SourceCodeUnit {
         // Apply the matched rule to the parent
         let applied_edit = self.apply_edit(&edit, parser);
         current_replace_range = get_replace_range(applied_edit);
-        current_rule = edit.matched_rule();
+        current_rule = edit.matched_rule().to_string();
         // Add the (tag, code_snippet) mapping to substitution table.
-        self.add_to_substitutions(edit.matches(), rules_store);
+        self.add_to_substitutions(edit.p_match().matches(), rules_store);
       } else {
         // No more parents found for cleanup
         break;
@@ -267,8 +267,7 @@ impl SourceCodeUnit {
       // Scope level is not "PArent" or "Global"
       if ![PARENT, GLOBAL].contains(&scope_level.as_str()) {
         for rule in rules {
-          let scope_query = ScopeGenerator::get_scope_query(
-            self.clone(),
+          let scope_query = self.get_scope_query(
             scope_level,
             current_match_range.start_byte,
             current_match_range.end_byte,
@@ -334,14 +333,14 @@ impl SourceCodeUnit {
   pub(crate) fn apply_edit(&mut self, edit: &Edit, parser: &mut Parser) -> InputEdit {
     // Get the tree_sitter's input edit representation
     let mut applied_edit =
-      self._apply_edit(edit.replacement_range(), edit.replacement_string(), parser);
+      self._apply_edit(edit.p_match().range(), edit.replacement_string(), parser);
     // Check if the edit kind is "DELETE something"
     if self.piranha_arguments.cleanup_comments().clone() && edit.replacement_string().is_empty() {
-      let deleted_at = edit.replacement_range().start_point.row;
+      let deleted_at = edit.p_match().range().start_point.row;
       if let Some(comment_range) = self.get_comment_at_line(
         deleted_at,
         self.piranha_arguments.cleanup_comments_buffer().clone(),
-        edit.replacement_range().start_byte,
+        edit.p_match().range().start_byte,
       ) {
         debug!("Deleting an associated comment");
         applied_edit = self._apply_edit(comment_range, "", parser);
@@ -379,8 +378,8 @@ impl SourceCodeUnit {
         relevant_nodes_found = true;
         let is_comment: bool = self
           .piranha_arguments
-          .language_name()
-          .is_comment(node.kind());
+          .piranha_language()
+          .is_comment(node.kind().to_string());
         relevant_nodes_are_comments = relevant_nodes_are_comments && is_comment;
         if is_comment {
           comment_range = Some(node.range());
@@ -515,8 +514,8 @@ impl SourceCodeUnit {
     &self, previous_edit_start: usize, previous_edit_end: usize, rules_store: &mut RuleStore,
     rules: &Vec<Rule>,
   ) -> Option<Edit> {
-    let number_of_ancestors_in_parent_scope =
-      *rules_store.get_number_of_ancestors_in_parent_scope();
+    let number_of_ancestors_in_parent_scope  =
+      *rules_store.piranha_args().number_of_ancestors_in_parent_scope();
     let changed_node = get_node_for_range(self.root_node(), previous_edit_start, previous_edit_end);
     debug!(
       "\n{}",
@@ -567,7 +566,7 @@ impl SourceCodeUnit {
         p_match.range().end_byte,
       );
 
-      if matched_node.is_satisfied(&self, &rule, p_match.matches(), rule_store) {
+      if self.is_satisfied(matched_node, &rule, p_match.matches(), rule_store) {
         trace!("Found match {:#?}", p_match);
         output.push(p_match);
       }
@@ -586,26 +585,50 @@ impl SourceCodeUnit {
       .get_matches(rule.clone(), rule_store, node, recursive)
       .first()
       .map(|p_match| {
-        let replacement = substitute_tags(rule.replace(), p_match.matches(), false);
-        let edit = Edit::new(p_match.clone(), replacement, rule.name());
+        let replacement_string = substitute_tags(rule.replace(), p_match.matches(), false);
+        let edit = Edit::new(p_match.clone(), replacement_string, rule.name());
         trace!("Rewrite found : {:#?}", edit);
         edit
       });
   }
-}
 
-pub(crate) trait SatisfiesConstraint {
-  // / Checks if the given rule satisfies the constraint of the rule, under the substitutions obtained upon matching `rule.query`
-  fn is_satisfied(
-    &self, source_code_unit: &SourceCodeUnit, rule: &Rule, substitutions: &HashMap<String, String>,
-    rule_store: &mut RuleStore,
-  ) -> bool;
-}
+  /// Generate a tree-sitter based query representing the scope of the previous edit.
+  /// We generate these scope queries by matching the rules provided in `<lang>_scopes.toml`.
+  pub(crate) fn get_scope_query(
+    &self, scope_level: &str, start_byte: usize, end_byte: usize, rules_store: &mut RuleStore,
+  ) -> String {
+    let root_node = self.root_node();
+    let mut changed_node = get_node_for_range(root_node, start_byte, end_byte);
+    // Get the scope matchers for `scope_level` from the `scope_config.toml`.
+    let scope_matchers = rules_store.get_scope_query_generators(scope_level);
+
+    // Match the `scope_matcher.matcher` to the parent
+    loop {
+      trace!(
+        "Getting scope {} for node kind {}",
+        scope_level,
+        changed_node.kind()
+      );
+      for m in &scope_matchers {
+        if let Some(p_match) =
+          changed_node.get_match_for_query(&self.code(), rules_store.query(&m.matcher()), false)
+        {
+          // Generate the scope query for the specific context by substituting the
+          // the tags with code snippets appropriately in the `generator` query.
+          return substitute_tags(m.generator().to_string(), p_match.matches(), true);
+        }
+      }
+      if let Some(parent) = changed_node.parent() {
+        changed_node = parent;
+      } else {
+        break;
+      }
+    }
+    panic!("Could not create scope query for {:?}", scope_level);
+  }
 
-/// Checks if the given rule satisfies the constraint of the rule, under the substitutions obtained upon matching `rule.query`
-impl SatisfiesConstraint for Node<'_> {
   fn is_satisfied(
-    &self, source_code_unit: &SourceCodeUnit, rule: &Rule, substitutions: &HashMap<String, String>,
+    &self, node: Node, rule: &Rule, substitutions: &HashMap<String, String>,
     rule_store: &mut RuleStore,
   ) -> bool {
     let updated_substitutions = &substitutions
@@ -614,14 +637,61 @@ impl SatisfiesConstraint for Node<'_> {
       .chain(rule_store.default_substitutions())
       .collect();
     rule.constraints().iter().all(|constraint| {
-      constraint.is_satisfied(
-        *self,
-        source_code_unit.clone(),
+      self._is_satisfied(
+        constraint.clone(),
+        node.clone(),
         rule_store,
         updated_substitutions,
       )
     })
   }
+
+  /// Checks if the node satisfies the constraints.
+  /// Constraint has two parts (i) `constraint.matcher` (ii) `constraint.query`.
+  /// This function traverses the ancestors of the given `node` until `constraint.matcher` matches
+  /// i.e. finds scope for constraint.
+  /// Within this scope it checks if the `constraint.query` DOES NOT MATCH any sub-tree.
+  fn _is_satisfied(
+    &self, constraint: Constraint, node: Node, rule_store: &mut RuleStore,
+    substitutions: &HashMap<String, String>,
+  ) -> bool {
+    let mut current_node = node;
+    // This ensures that the below while loop considers the current node too when checking for constraints.
+    // It does not make sense to check for constraint if current node is a "leaf" node.
+    if node.child_count() > 0 {
+      current_node = node.child(0).unwrap();
+    }
+    // Get the scope_node of the constraint (`scope.matcher`)
+    let mut matched_matcher = false;
+    while let Some(parent) = current_node.parent() {
+
+      let matcher_query_str = substitute_tags(constraint.matcher().to_string(), substitutions, true);
+      if let Some(p_match) =
+        parent.get_match_for_query(&self.code(), rule_store.query(&matcher_query_str), false)
+      {
+        matched_matcher = true;
+        let scope_node = get_node_for_range(
+          self.root_node(),
+          p_match.range().start_byte,
+          p_match.range().end_byte,
+        );
+        for query_with_holes in constraint.queries() {
+          let query_str = substitute_tags(query_with_holes.to_string(), substitutions, true);
+          let query = &rule_store.query(&query_str);
+          // If this query matches anywhere within the scope, return false.
+          if scope_node
+            .get_match_for_query(&self.code(), query, true)
+            .is_some()
+          {
+            return false;
+          }
+        }
+        break;
+      }
+      current_node = parent;
+    }
+    matched_matcher
+  }
 }
 
 #[cfg(test)]
diff --git a/polyglot/piranha/src/utilities/tree_sitter_utilities.rs b/polyglot/piranha/src/utilities/tree_sitter_utilities.rs
index b25495c65..584e17b69 100644
--- a/polyglot/piranha/src/utilities/tree_sitter_utilities.rs
+++ b/polyglot/piranha/src/utilities/tree_sitter_utilities.rs
@@ -13,62 +13,15 @@ Copyright (c) 2022 Uber Technologies, Inc.
 
 //! Defines the traits containing with utility functions that interface with tree-sitter.
 
-use crate::{
-  models::{matches::Match},
-  utilities::MapOfVec,
-};
+use crate::{models::matches::Match, utilities::MapOfVec};
 use colored::Colorize;
 use itertools::Itertools;
 use log::debug;
 use std::collections::HashMap;
-#[cfg(test)]
-use tree_sitter::Parser;
-use tree_sitter::{InputEdit, Language, Node, Point, Query, QueryCapture, QueryCursor, Range};
+use tree_sitter::{InputEdit, Node, Point, Query, QueryCapture, QueryCursor, Range};
 
 use super::eq_without_whitespace;
 
-pub(crate) trait TreeSitterHelpers {
-  /// Gets the tree-sitter language model.
-  fn get_language(&self) -> Language;
-  /// Compiles query string to `tree_sitter::Query`
-  fn create_query(&self, language: Language) -> Query;
-  /// Determines if the given node kind is a comment for the respective language (`self`)
-  fn is_comment(&self, kind: &str) -> bool;
-}
-
-impl TreeSitterHelpers for String {
-  fn create_query(&self, language: Language) -> Query {
-    let query = Query::new(language, self.as_str());
-    if let Ok(q) = query {
-      return q;
-    }
-    panic!("Could not parse the query : {} {:?}", self, query.err());
-  }
-
-  fn get_language(&self) -> Language {
-    match self.as_str() {
-      "java" => tree_sitter_java::language(),
-      "go" => tree_sitter_go::language(),
-      "kt" => tree_sitter_kotlin::language(),
-      "py" => tree_sitter_python::language(),
-      "swift" => tree_sitter_swift::language(),
-      "strings" => tree_sitter_strings::language(),
-      "ts" => tree_sitter_typescript::language_typescript(),
-      "tsx" => tree_sitter_typescript::language_tsx(),
-      _ => panic!("Language not supported"),
-    }
-  }
-
-  fn is_comment(&self, kind: &str) -> bool {
-    match self.as_str() {
-      "java" => kind.eq("line_comment") || kind.eq("block_comment"),
-      "kt" => kind.eq("comment"),
-      "swift" => kind.eq("comment") || kind.eq("multiline_comment"),
-      _ => false,
-    }
-  }
-}
-
 #[rustfmt::skip]
 pub(crate) trait PiranhaHelpers {
 
@@ -105,7 +58,6 @@ pub(crate) trait PiranhaHelpers {
 }
 
 impl PiranhaHelpers for Node<'_> {
-
   fn get_match_for_query(
     &self, source_code: &str, query: &Query, recursive: bool,
   ) -> Option<Match> {
@@ -385,15 +337,6 @@ pub(crate) fn get_replace_range(input_edit: InputEdit) -> Range {
   }
 }
 
-#[cfg(test)]
-pub(crate) fn get_parser(language: String) -> Parser {
-  let mut parser = Parser::new();
-  parser
-    .set_language(language.get_language())
-    .expect("Could not set the language for the parser.");
-  parser
-}
-
 #[cfg(test)]
 #[path = "unit_tests/tree_sitter_utilities_test.rs"]
 mod tree_sitter_utilities_test;


Test patch:
diff --git a/polyglot/piranha/src/models/unit_tests/rule_test.rs b/polyglot/piranha/src/models/unit_tests/rule_test.rs
index fd9286cd2..f80d0a810 100644
--- a/polyglot/piranha/src/models/unit_tests/rule_test.rs
+++ b/polyglot/piranha/src/models/unit_tests/rule_test.rs
@@ -10,13 +10,12 @@ Copyright (c) 2022 Uber Technologies, Inc.
  express or implied. See the License for the specific language governing permissions and
  limitations under the License.
 */
+use crate::models::language::PiranhaLanguage;
 use std::collections::HashSet;
-
 use {
   super::Rule,
-  crate::{
-    models::{constraint::Constraint, rule_store::RuleStore, source_code_unit::SourceCodeUnit},
-    utilities::tree_sitter_utilities::get_parser,
+  crate::models::{
+    constraint::Constraint, rule_store::RuleStore, source_code_unit::SourceCodeUnit,
   },
   std::collections::HashMap,
   std::path::PathBuf,
@@ -83,7 +82,7 @@ fn test_get_edit_positive_recursive() {
 
   let mut rule_store = RuleStore::default();
 
-  let mut parser = get_parser(String::from("java"));
+  let mut parser = PiranhaLanguage::from("java").parser();
 
   let source_code_unit = SourceCodeUnit::new(
     &mut parser,
@@ -130,7 +129,7 @@ fn test_get_edit_negative_recursive() {
         }";
 
   let mut rule_store = RuleStore::default();
-  let mut parser = get_parser(String::from("java"));
+  let mut parser = PiranhaLanguage::from("java").parser();
 
   let source_code_unit = SourceCodeUnit::new(
     &mut parser,
@@ -170,7 +169,7 @@ fn test_get_edit_for_context_positive() {
 
   let mut rule_store = RuleStore::default();
 
-  let mut parser = get_parser(String::from("java"));
+  let mut parser = PiranhaLanguage::from("java").parser();
 
   let source_code_unit = SourceCodeUnit::new(
     &mut parser,
@@ -210,7 +209,7 @@ fn test_get_edit_for_context_negative() {
 
   let mut rule_store = RuleStore::default();
 
-  let mut parser = get_parser(String::from("java"));
+  let mut parser = PiranhaLanguage::from("java").parser();
 
   let source_code_unit = SourceCodeUnit::new(
     &mut parser,
diff --git a/polyglot/piranha/src/models/unit_tests/scopes_test.rs b/polyglot/piranha/src/models/unit_tests/scopes_test.rs
index 4288da211..df268743b 100644
--- a/polyglot/piranha/src/models/unit_tests/scopes_test.rs
+++ b/polyglot/piranha/src/models/unit_tests/scopes_test.rs
@@ -1,3 +1,5 @@
+use crate::models::language::PiranhaLanguage;
+
 /*
 Copyright (c) 2022 Uber Technologies, Inc.
 
@@ -15,7 +17,6 @@ use {
   crate::{
     models::{rule_store::RuleStore, source_code_unit::SourceCodeUnit},
     utilities::eq_without_whitespace,
-    utilities::tree_sitter_utilities::get_parser,
   },
   std::{collections::HashMap, path::PathBuf},
 };
@@ -97,7 +98,7 @@ fn test_get_scope_query_positive() {
     }";
   
   let mut rule_store =  _get_rule_store();
-  let mut parser = get_parser(String::from("java"));
+  let mut parser = PiranhaLanguage::from("java").parser();
 
   let source_code_unit = SourceCodeUnit::new(
     &mut parser,
@@ -107,8 +108,7 @@ fn test_get_scope_query_positive() {
     rule_store.piranha_args(),
   );
 
-  let scope_query_method = ScopeGenerator::get_scope_query(
-    source_code_unit.clone(),
+  let scope_query_method = source_code_unit.get_scope_query(
     "Method",
     133,
     134,
@@ -136,7 +136,7 @@ fn test_get_scope_query_positive() {
   ));
 
   let scope_query_class =
-    ScopeGenerator::get_scope_query(source_code_unit, "Class", 133, 134, &mut rule_store);
+  source_code_unit.get_scope_query("Class", 133, 134, &mut rule_store);
   assert!(eq_without_whitespace(
     scope_query_class.as_str(),
     "(
@@ -160,7 +160,7 @@ fn test_get_scope_query_negative() {
       }
     }";
   let mut rule_store = _get_rule_store();
-  let mut parser = get_parser(String::from("java"));
+  let mut parser = PiranhaLanguage::from("java").parser();
 
   let source_code_unit = SourceCodeUnit::new(
     &mut parser,
@@ -170,5 +170,5 @@ fn test_get_scope_query_negative() {
     rule_store.piranha_args(),
   );
 
-  let _ = ScopeGenerator::get_scope_query(source_code_unit, "Method", 9, 10, &mut rule_store);
+  let _ = source_code_unit.get_scope_query("Method", 9, 10, &mut rule_store);
 }
diff --git a/polyglot/piranha/src/models/unit_tests/source_code_unit_test.rs b/polyglot/piranha/src/models/unit_tests/source_code_unit_test.rs
index c32627c20..30356393e 100644
--- a/polyglot/piranha/src/models/unit_tests/source_code_unit_test.rs
+++ b/polyglot/piranha/src/models/unit_tests/source_code_unit_test.rs
@@ -12,7 +12,7 @@ Copyright (c) 2022 Uber Technologies, Inc.
 */
 use std::{
   fs::{self},
-  io,
+  io, collections::HashSet,
 };
 
 use itertools::Itertools;
@@ -20,12 +20,11 @@ use tempdir::TempDir;
 
 use tree_sitter::Parser;
 
-use crate::models::piranha_arguments::{PiranhaArguments, PiranhaArgumentsBuilder};
+use crate::models::{piranha_arguments::{PiranhaArguments, PiranhaArgumentsBuilder}, rule::Rule, constraint::Constraint, rule_store::RuleStore, language::PiranhaLanguage};
 use {
   super::SourceCodeUnit,
   crate::{
     models::edit::Edit, utilities::eq_without_whitespace,
-    utilities::tree_sitter_utilities::get_parser,
   },
   std::{collections::HashMap, path::PathBuf},
   tree_sitter::Range,
@@ -39,7 +38,7 @@ impl SourceCodeUnit {
       &HashMap::new(),
       PathBuf::new().as_path(),
       &PiranhaArgumentsBuilder::default()
-        .language_name(language_name)
+        .language(vec![language_name])
         .build()
         .unwrap(),
     )
@@ -64,6 +63,10 @@ fn range(
   }
 }
 
+fn get_java_tree_sitter_language() -> PiranhaLanguage {
+   PiranhaLanguage::from("java")
+}
+
 /// Positive test of an edit being applied  given replacement range  and replacement string.
 #[test]
 fn test_apply_edit_positive() {
@@ -77,13 +80,13 @@ fn test_apply_edit_positive() {
       }
     }";
 
-  let language_name = String::from("java");
-  let mut parser = get_parser(language_name.to_string());
+  let java = get_java_tree_sitter_language();
+  let mut parser = java.parser();
 
-  let mut source_code_unit = SourceCodeUnit::default(source_code, &mut parser, language_name);
+  let mut source_code_unit = SourceCodeUnit::default(source_code, &mut parser, java.name().to_string());
 
   let _ = source_code_unit.apply_edit(
-    &Edit::from(range(49, 78, 3, 9, 3, 38), String::new()),
+    &Edit::from(range(49, 78, 3, 9, 3, 38)),
     &mut parser,
   );
   assert!(eq_without_whitespace(
@@ -106,12 +109,12 @@ fn test_apply_edit_negative() {
       }
     }";
 
-  let language_name = String::from("java");
-  let mut parser = get_parser(language_name.to_string());
-  let mut source_code_unit = SourceCodeUnit::default(source_code, &mut parser, language_name);
+  let java = get_java_tree_sitter_language();
+  let mut parser = java.parser();
+  let mut source_code_unit = SourceCodeUnit::default(source_code, &mut parser, java.name().to_string());
 
   let _ = source_code_unit.apply_edit(
-    &Edit::from(range(1000, 2000, 0, 0, 0, 0), String::new()),
+    &Edit::from(range(1000, 2000, 0, 0, 0, 0)),
     &mut parser,
   );
 }
@@ -127,13 +130,13 @@ fn test_apply_edit_comma_handling_via_grammar() {
       }
     }";
 
-  let language_name = String::from("java");
-  let mut parser = get_parser(language_name.to_string());
+    let java = get_java_tree_sitter_language();
+  let mut parser = java.parser();
 
-  let mut source_code_unit = SourceCodeUnit::default(source_code, &mut parser, language_name);
+  let mut source_code_unit = SourceCodeUnit::default(source_code, &mut parser, java.name().to_string());
 
   let _ = source_code_unit.apply_edit(
-    &Edit::from(range(37, 47, 2, 26, 2, 36), String::new()),
+    &Edit::from(range(37, 47, 2, 26, 2, 36)),
     &mut parser,
   );
   assert!(eq_without_whitespace(
@@ -154,14 +157,14 @@ fn test_apply_edit_comma_handling_via_regex() {
   }
 }";
 
-  let language_name = String::from("swift");
+  let swift = PiranhaLanguage::from("swift");
 
-  let mut parser = get_parser(language_name.to_string());
+  let mut parser = swift.parser();
 
-  let mut source_code_unit = SourceCodeUnit::default(source_code, &mut parser, language_name);
+  let mut source_code_unit = SourceCodeUnit::default(source_code, &mut parser, swift.name().to_string());
 
   let _ = source_code_unit.apply_edit(
-    &Edit::from(range(59, 75, 3, 23, 3, 41), String::new()),
+    &Edit::from(range(59, 75, 3, 23, 3, 41)),
     &mut parser,
   );
   assert!(eq_without_whitespace(
@@ -173,13 +176,13 @@ fn execute_persist_in_temp_folder(
   source_code: &str, args: &PiranhaArguments,
   check_predicate: &dyn Fn(&TempDir) -> Result<bool, io::Error>,
 ) -> Result<bool, io::Error> {
-  let language_name = String::from("java");
-  let mut parser = get_parser(language_name.to_string());
+  let java = get_java_tree_sitter_language();
+  let mut parser = java.parser();
   let tmp_dir = TempDir::new("example")?;
   let file_path = &tmp_dir.path().join("Sample1.java");
   _ = fs::write(&file_path.as_path(), source_code);
   let piranha_args = PiranhaArgumentsBuilder::default()
-    .language_name(language_name)
+    .language(vec![java.name().to_string()])
     .build()
     .unwrap();
   let source_code_unit = SourceCodeUnit::new(
@@ -305,3 +308,138 @@ fn test_persist_do_not_delete_consecutive_lines() -> Result<(), io::Error> {
   assert!(execute_persist_in_temp_folder(source_code, &args, &check)?);
   Ok(())
 }
+
+#[test]
+fn test_satisfies_constraints_positive() {
+  let rule = Rule::new(
+    "test",
+    "(
+      ((local_variable_declaration
+                      declarator: (variable_declarator
+                                          name: (_) @variable_name
+                                          value: [(true) (false)] @init)) @variable_declaration)
+      )",
+    "variable_declaration",
+    "",
+    HashSet::new(),
+    HashSet::from([Constraint::new(
+      String::from("(method_declaration) @md"),
+      vec![String::from(
+        "(
+         ((assignment_expression
+                         left: (_) @a.lhs
+                         right: (_) @a.rhs) @assignment)
+         (#eq? @a.lhs \"@variable_name\")
+         (#not-eq? @a.rhs \"@init\")
+       )",
+      )],
+    )]),
+  );
+  let source_code = "class Test {
+      pub void foobar(){
+        boolean isFlagTreated = true;
+        isFlagTreated = true;
+        if (isFlagTreated) {
+        // Do something;
+        }
+       }
+      }";
+
+  let mut rule_store = RuleStore::default();
+  let java = get_java_tree_sitter_language();
+  let mut parser = java.parser();
+  let piranha_args = PiranhaArgumentsBuilder::default()
+    .language(vec![java.name().to_string()])
+    .build()
+    .unwrap();
+  let source_code_unit = SourceCodeUnit::new(
+    &mut parser,
+    source_code.to_string(),
+    &HashMap::new(),
+    PathBuf::new().as_path(),
+    &piranha_args,
+  );
+
+  let node = &source_code_unit
+    .root_node()
+    .descendant_for_byte_range(50, 72)
+    .unwrap();
+
+  assert!(source_code_unit.is_satisfied(
+    node.clone(),
+    &rule,
+    &HashMap::from([
+      ("variable_name".to_string(), "isFlagTreated".to_string()),
+      ("init".to_string(), "true".to_string())
+    ]),
+    &mut rule_store,
+  ));
+}
+
+#[test]
+fn test_satisfies_constraints_negative() {
+  let rule = Rule::new(
+    "test",
+    "(
+      ((local_variable_declaration
+                      declarator: (variable_declarator
+                                          name: (_) @variable_name
+                                          value: [(true) (false)] @init)) @variable_declaration)
+      )",
+    "variable_declaration",
+    "",
+    HashSet::new(),
+    HashSet::from([Constraint::new(
+      String::from("(method_declaration) @md"),
+      vec![String::from(
+        "(
+         ((assignment_expression
+                         left: (_) @a.lhs
+                         right: (_) @a.rhs) @assignment)
+         (#eq? @a.lhs \"@variable_name\")
+         (#not-eq? @a.rhs \"@init\")
+       )",
+      )],
+    )]),
+  );
+  let source_code = "class Test {
+      pub void foobar(){
+        boolean isFlagTreated = true;
+        isFlagTreated = false;
+        if (isFlagTreated) {
+        // Do something;
+        }
+       }
+      }";
+
+  let mut rule_store = RuleStore::default();
+  let java = get_java_tree_sitter_language();
+  let mut parser = java.parser();
+  let piranha_arguments = &PiranhaArgumentsBuilder::default()
+    .language(vec![java.name().to_string()])
+    .build()
+    .unwrap();
+  let source_code_unit = SourceCodeUnit::new(
+    &mut parser,
+    source_code.to_string(),
+    &HashMap::new(),
+    PathBuf::new().as_path(),
+    piranha_arguments,
+  );
+
+  let node = &source_code_unit
+    .root_node()
+    .descendant_for_byte_range(50, 72)
+    .unwrap();
+
+  assert!(!source_code_unit.is_satisfied(
+    node.clone(),
+    &rule,
+    &HashMap::from([
+      ("variable_name".to_string(), "isFlagTreated".to_string()),
+      ("init".to_string(), "true".to_string())
+    ]),
+    &mut rule_store,
+  ));
+}
+
diff --git a/polyglot/piranha/src/tests/mod.rs b/polyglot/piranha/src/tests/mod.rs
index 68943ff52..a623bb35b 100644
--- a/polyglot/piranha/src/tests/mod.rs
+++ b/polyglot/piranha/src/tests/mod.rs
@@ -11,15 +11,13 @@ Copyright (c) 2022 Uber Technologies, Inc.
  limitations under the License.
 */
 
-use std::path::{Path, PathBuf};
-
-use log::error;
-
-use crate::config::CommandLineArguments;
 use crate::execute_piranha;
 use crate::models::piranha_arguments::PiranhaArguments;
+use crate::models::piranha_input::PiranhaInput;
 use crate::models::piranha_output::PiranhaOutputSummary;
 use crate::utilities::{eq_without_whitespace, find_file, read_file};
+use log::error;
+use std::path::{Path, PathBuf};
 
 mod test_piranha_java;
 mod test_piranha_kt;
@@ -47,14 +45,17 @@ fn initialize() {
 // Runs a piranha over the target `<relative_path_to_tests>/input` (using configurations `<relative_path_to_tests>/configuration`)
 // and checks if the number of matches == `number_of_matches`.
 fn run_match_test(relative_path_to_tests: &str, number_of_matches: usize) {
-  let path_to_test_ff = format!("test-resources/{relative_path_to_tests}");
+  let path_to_configurations = format!("test-resources/{relative_path_to_tests}/configurations/");
+  let path_to_codebase = format!("test-resources/{relative_path_to_tests}/input/");
 
-  let args = PiranhaArguments::new(CommandLineArguments {
-    path_to_codebase: format!("{path_to_test_ff}/input/"),
-    path_to_configurations: format!("{path_to_test_ff}/configurations/"),
-    path_to_output_summary: None,
+  let piranha_input = PiranhaInput::API {
+    path_to_codebase,
+    path_to_configurations,
     dry_run: true,
-  });
+  };
+  let args = PiranhaArguments::from(piranha_input);
+  print!("{:?}", args);
+
   let output_summaries = execute_piranha(&args);
 
   assert_eq!(
@@ -70,14 +71,17 @@ fn run_match_test(relative_path_to_tests: &str, number_of_matches: usize) {
 // and checks if the output of piranha is same as `<relative_path_to_tests>/expected`.
 // It also asserts the number of changed files in the expected output.
 fn run_rewrite_test(relative_path_to_tests: &str, n_files_changed: usize) {
-  let path_to_test_ff = format!("test-resources/{relative_path_to_tests}");
+  let path_to_configurations = format!("test-resources/{relative_path_to_tests}/configurations/");
+  let path_to_codebase = format!("test-resources/{relative_path_to_tests}/input/");
 
-  let args = PiranhaArguments::new(CommandLineArguments {
-    path_to_codebase: format!("{path_to_test_ff}/input/"),
-    path_to_configurations: format!("{path_to_test_ff}/configurations/"),
-    path_to_output_summary: None,
+  let piranha_input = PiranhaInput::API {
+    path_to_codebase,
+    path_to_configurations,
     dry_run: true,
-  });
+  };
+  let args = PiranhaArguments::from(piranha_input);
+  print!("{:?}", args);
+
   let output_summaries = execute_piranha(&args);
   // Checks if there are any rewrites performed for the file
   assert!(
@@ -89,8 +93,8 @@ fn run_rewrite_test(relative_path_to_tests: &str, n_files_changed: usize) {
   );
 
   assert_eq!(output_summaries.len(), n_files_changed);
-  let path_to_expected =
-    Path::new(env!("CARGO_MANIFEST_DIR")).join(format!("{path_to_test_ff}/expected"));
+  let path_to_expected = Path::new(env!("CARGO_MANIFEST_DIR"))
+    .join(format!("test-resources/{relative_path_to_tests}/expected"));
   check_result(output_summaries, path_to_expected);
 }
 
diff --git a/polyglot/piranha/src/utilities/unit_tests/tree_sitter_utilities_test.rs b/polyglot/piranha/src/utilities/unit_tests/tree_sitter_utilities_test.rs
index a0ca3aed5..1f23ef370 100644
--- a/polyglot/piranha/src/utilities/unit_tests/tree_sitter_utilities_test.rs
+++ b/polyglot/piranha/src/utilities/unit_tests/tree_sitter_utilities_test.rs
@@ -10,21 +10,13 @@ Copyright (c) 2022 Uber Technologies, Inc.
  express or implied. See the License for the specific language governing permissions and
  limitations under the License.
 */
-use std::{
-  collections::{HashMap, HashSet},
-  path::PathBuf,
-};
+use std::collections::HashMap;
 
 use tree_sitter::Query;
 
-use crate::models::{piranha_arguments::PiranhaArgumentsBuilder, source_code_unit::SatisfiesConstraint};
+use crate::models::language::PiranhaLanguage;
 
-use {
-  super::{get_parser, substitute_tags, PiranhaHelpers, TreeSitterHelpers},
-  crate::models::{
-    constraint::Constraint, rule::Rule, rule_store::RuleStore, source_code_unit::SourceCodeUnit,
-  },
-};
+use super::{substitute_tags, PiranhaHelpers};
 
 #[test]
 fn test_get_all_matches_for_query_positive() {
@@ -42,9 +34,9 @@ fn test_get_all_matches_for_query_positive() {
         }
       }
     "#;
-  let language_name = String::from("java");
+  let language = PiranhaLanguage::from("java");
   let query = Query::new(
-    language_name.get_language(),
+    *language.language(),
     r#"((
         (method_invocation 
           name : (_) @name
@@ -62,7 +54,7 @@ fn test_get_all_matches_for_query_positive() {
   )
   .unwrap();
 
-  let mut parser = get_parser(String::from("java"));
+  let mut parser = PiranhaLanguage::from("java").parser();
   let ast = parser
     .parse(&source_code, None)
     .expect("Could not parse code");
@@ -93,9 +85,9 @@ fn test_get_all_matches_for_query_negative() {
         }
       }
     "#;
-  let language_name = String::from("java");
+  let language = PiranhaLanguage::from("java");
   let query = Query::new(
-    language_name.get_language(),
+    *language.language(),
     r#"((
         (method_invocation 
           name : (_) @name
@@ -113,7 +105,7 @@ fn test_get_all_matches_for_query_negative() {
   )
   .unwrap();
 
-  let mut parser = get_parser(String::from("java"));
+  let mut parser = PiranhaLanguage::from("java").parser();
   let ast = parser
     .parse(&source_code, None)
     .expect("Could not parse code");
@@ -128,140 +120,6 @@ fn test_get_all_matches_for_query_negative() {
   assert!(matches.is_empty());
 }
 
-#[test]
-fn test_satisfies_constraints_positive() {
-  let rule = Rule::new(
-    "test",
-    "(
-      ((local_variable_declaration
-                      declarator: (variable_declarator
-                                          name: (_) @variable_name
-                                          value: [(true) (false)] @init)) @variable_declaration)
-      )",
-    "variable_declaration",
-    "",
-    HashSet::new(),
-    HashSet::from([Constraint::new(
-      String::from("(method_declaration) @md"),
-      vec![String::from(
-        "(
-         ((assignment_expression
-                         left: (_) @a.lhs
-                         right: (_) @a.rhs) @assignment)
-         (#eq? @a.lhs \"@variable_name\")
-         (#not-eq? @a.rhs \"@init\")
-       )",
-      )],
-    )]),
-  );
-  let source_code = "class Test {
-      pub void foobar(){
-        boolean isFlagTreated = true;
-        isFlagTreated = true;
-        if (isFlagTreated) {
-        // Do something;
-        }
-       }
-      }";
-
-  let mut rule_store = RuleStore::default();
-  let language_name = String::from("java");
-  let mut parser = get_parser(language_name.to_string());
-  let piranha_args = PiranhaArgumentsBuilder::default()
-    .language_name(language_name)
-    .build()
-    .unwrap();
-  let source_code_unit = SourceCodeUnit::new(
-    &mut parser,
-    source_code.to_string(),
-    &HashMap::new(),
-    PathBuf::new().as_path(),
-    &piranha_args,
-  );
-
-  let node = &source_code_unit
-    .root_node()
-    .descendant_for_byte_range(50, 72)
-    .unwrap();
-
-  assert!(node.is_satisfied(
-    &source_code_unit,
-    &rule,
-    &HashMap::from([
-      ("variable_name".to_string(), "isFlagTreated".to_string()),
-      ("init".to_string(), "true".to_string())
-    ]),
-    &mut rule_store,
-  ));
-}
-
-#[test]
-fn test_satisfies_constraints_negative() {
-  let rule = Rule::new(
-    "test",
-    "(
-      ((local_variable_declaration
-                      declarator: (variable_declarator
-                                          name: (_) @variable_name
-                                          value: [(true) (false)] @init)) @variable_declaration)
-      )",
-    "variable_declaration",
-    "",
-    HashSet::new(),
-    HashSet::from([Constraint::new(
-      String::from("(method_declaration) @md"),
-      vec![String::from(
-        "(
-         ((assignment_expression
-                         left: (_) @a.lhs
-                         right: (_) @a.rhs) @assignment)
-         (#eq? @a.lhs \"@variable_name\")
-         (#not-eq? @a.rhs \"@init\")
-       )",
-      )],
-    )]),
-  );
-  let source_code = "class Test {
-      pub void foobar(){
-        boolean isFlagTreated = true;
-        isFlagTreated = false;
-        if (isFlagTreated) {
-        // Do something;
-        }
-       }
-      }";
-
-  let mut rule_store = RuleStore::default();
-  let language_name = String::from("java");
-  let mut parser = get_parser(language_name.to_string());
-  let piranha_arguments = &PiranhaArgumentsBuilder::default()
-    .language_name(language_name)
-    .build()
-    .unwrap();
-  let source_code_unit = SourceCodeUnit::new(
-    &mut parser,
-    source_code.to_string(),
-    &HashMap::new(),
-    PathBuf::new().as_path(),
-    piranha_arguments,
-  );
-
-  let node = &source_code_unit
-    .root_node()
-    .descendant_for_byte_range(50, 72)
-    .unwrap();
-
-  assert!(!node.is_satisfied(
-    &source_code_unit,
-    &rule,
-    &HashMap::from([
-      ("variable_name".to_string(), "isFlagTreated".to_string()),
-      ("init".to_string(), "true".to_string())
-    ]),
-    &mut rule_store,
-  ));
-}
-
 #[test]
 fn test_substitute_tags() {
   let substitutions = HashMap::from([