use std::collections::HashMap;
use crate::Value;
pub struct Python<'a> {
ir: &'a HashMap<String, Value>,
}
impl<'a> Python<'a> {
pub fn new(ir: &'a HashMap<String, Value>) -> Self {
Self { ir }
}
fn value_to_code(&self, v: &Value) -> String {
match v {
Value::Str(s) => {
if s.starts_with('@') {
s[1..].to_string()
} else {
format!("'{}'", s.replace('\'', "\\'"))
}
},
Value::Int(i) => i.to_string(),
Value::Bool(b) => if *b { "True" } else { "False" }.to_string(),
Value::Null => "None".to_string(),
Value::List(items) => {
let inner: Vec<String> = items.iter()
.map(|item| self.value_to_code(item))
.collect();
format!("[{}]", inner.join(", "))
}
Value::Map(map) => {
let inner: Vec<String> = map.iter()
.map(|(k, v)| format!("'{}': {}", k, self.value_to_code(v)))
.collect();
format!("{{{}}}", inner.join(", "))
}
}
}
fn process_imports(&self, code: &mut String) {
if let Some(Value::List(imports)) = self.ir.get("imports") {
let mut names: Vec<&str> = imports.iter()
.filter_map(|i| match i {
Value::Str(s) => Some(s.as_str()),
_ => None,
})
.collect();
names.sort();
for name in &names {
code.push_str(&format!("import {}\n", name));
}
if !names.is_empty() {
code.push('\n');
}
}
}
fn process_variables(&self, code: &mut String) {
let mut var_keys: Vec<&String> = self.ir.keys()
.filter(|k| k.starts_with("var:"))
.collect();
var_keys.sort();
for key in var_keys {
if let Value::Map(map) = &self.ir[key] {
if let Some(val) = map.get("value") {
let var_name = &key[4..];
code.push_str(&format!("{} = {}\n", var_name, self.value_to_code(val)));
}
}
}
}
fn process_calls(&self, code: &mut String) {
let aliases = crate::transformers::replaceable_names::python_aliases();
let mut call_keys: Vec<&String> = self.ir.keys()
.filter(|k| k.starts_with("call:"))
.collect();
call_keys.sort();
for key in call_keys {
let func_name = &key[5..];
let aliased_name = aliases.get(func_name).map(|s| s.as_str()).unwrap_or(func_name);
if let Value::Map(params) = &self.ir[key] {
let args = if let Some(Value::List(args)) = params.get("args") {
args.iter()
.map(|arg| self.value_to_code(arg))
.collect::<Vec<_>>()
.join(", ")
} else {
String::new()
};
code.push_str(&format!("{}({})\n", aliased_name, args));
}
}
}
pub fn generate(&self) -> String {
let mut code = String::new();
self.process_imports(&mut code);
self.process_variables(&mut code);
if !code.is_empty() && !code.ends_with('\n') {
code.push('\n');
}
self.process_calls(&mut code);
code
}
}