use sesh_ast::*;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum CodegenError {
#[error("IO error during code generation: {0}")]
Io(#[from] std::io::Error),
#[error("Failed to invoke assembler/linker: {0}")]
LinkerError(String),
#[error("Unsupported syntax construct for native x86_64 codegen: {0}")]
Unsupported(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TargetArch {
#[default]
X86_64Linux,
AArch64Linux,
BaremetalCpp,
Ros2Cpp,
}
impl TargetArch {
pub fn from_str(s: &str) -> Self {
match s.to_lowercase().as_str() {
"aarch64" | "arm64" | "aarch64-linux" => Self::AArch64Linux,
"baremetal-c" | "cpp" | "c++" | "arduino" | "stm32" => Self::BaremetalCpp,
"ros2-cpp" | "ros2" | "ros2-node" => Self::Ros2Cpp,
_ => Self::X86_64Linux,
}
}
}
pub struct BaremetalCppCodegen {
code: String,
indent_level: usize,
}
impl BaremetalCppCodegen {
pub fn new() -> Self {
Self {
code: String::new(),
indent_level: 0,
}
}
fn indent(&mut self) {
for _ in 0..self.indent_level {
self.code.push_str(" ");
}
}
pub fn generate_cpp(&mut self, program: &Program) -> Result<String, CodegenError> {
self.code.clear();
self.code.push_str("// ============================================================================\n");
self.code.push_str("// AUTO-GENERATED BY SESH 1.0.0 COMPILER (Bare-Metal C++20 Microcontroller Target)\n");
self.code.push_str("// ============================================================================\n\n");
self.code.push_str("#include <cstdint>\n#include <iostream>\n#include <string>\n#include <vector>\n#include <cmath>\n\n");
self.code.push_str("namespace sesh_rt {\n");
self.code.push_str(" inline void gpio_write(uint32_t pin, bool level) { std::cout << \"[HW:GPIO] Pin \" << pin << \" -> \" << (level ? \"HIGH\" : \"LOW\") << \"\\n\"; }\n");
self.code.push_str(" inline void pwm_write(uint32_t pin, uint32_t duty) { std::cout << \"[HW:PWM] Pin \" << pin << \" -> Duty \" << duty << \"%\\n\"; }\n");
self.code.push_str(" inline void ros_publish(const std::string& topic, const std::string& msg) { std::cout << \"[ROS2:PUB] /\" << topic << \" : \" << msg << \"\\n\"; }\n");
self.code.push_str(" inline void ros_init(const std::string& name) { std::cout << \"[ROS2:INIT] \" << name << \"\\n\"; }\n");
self.code.push_str(" inline void servo_write_angle(uint32_t pin, double angle) { std::cout << \"[HW:SERVO] Pin \" << pin << \" -> \" << angle << \"°\\n\"; }\n");
self.code.push_str(" inline void stepper_set_microsteps(uint32_t ch, uint32_t div) {}\n");
self.code.push_str(" inline void stepper_step_angle(uint32_t ch, int32_t steps, double rpm) {}\n");
self.code.push_str(" inline void watchdog_start(uint32_t ms) {}\n");
self.code.push_str(" inline void watchdog_reset() {}\n");
self.code.push_str(" inline double kinematics_ik_3dof_arm(double x, double y, double z) { return 45.0; }\n");
self.code.push_str(" inline double kinematics_diff_drive_ik(double v, double w) { return 50.0; }\n");
self.code.push_str(" inline double kalman_ekf_update(double x, double z, double p, double r) { return x; }\n");
self.code.push_str(" inline double kalman_fuse_imu_gps(double lat, double lon, double yaw) { return lat; }\n");
self.code.push_str(" inline double control_lqr_compute(double ex, double ev, double k1, double k2) { return 0.0; }\n");
self.code.push_str(" inline double gps_distance_to_waypoint(double l1, double n1, double l2, double n2) { return 100.0; }\n");
self.code.push_str(" inline double lidar_scan_360(uint32_t port) { return 0.0; }\n");
self.code.push_str(" inline double lidar_find_closest_obstacle(double s) { return 45.0; }\n");
self.code.push_str(" inline int camera_capture() { return 0; }\n");
self.code.push_str(" inline std::string ai_classify_obstacle(double d, double c) { return \"Person\"; }\n");
self.code.push_str(" inline double pid_compute(double sp, double act, double kp, double ki, double kd) {\n");
self.code.push_str(" static double integral = 0.0; static double prev_error = 0.0;\n");
self.code.push_str(" double dt = 0.005; double err = sp - act; integral += err * dt;\n");
self.code.push_str(" if (integral > 100.0) integral = 100.0; if (integral < -100.0) integral = -100.0;\n");
self.code.push_str(" double deriv = (err - prev_error) / dt; prev_error = err;\n");
self.code.push_str(" return (kp * err) + (ki * integral) + (kd * deriv);\n }\n");
self.code.push_str(" inline double ultrasonic_read(uint32_t trig, uint32_t echo) { return 45.0; }\n");
self.code.push_str("}\n\n");
for item in &program.items {
match item {
Item::Robot(robot) => {
self.code.push_str(&format!("class {} {{\npublic:\n", robot.name));
for pin in &robot.pins {
self.code.push_str(&format!(
" static constexpr uint32_t pin_{} = {};\n",
pin.name, pin.pin_num
));
}
self.indent_level = 1;
for method in &robot.methods {
self.gen_fn_cpp(method)?;
}
for task in &robot.tasks {
self.indent();
self.code.push_str(&format!(
"void task_{}() {{ // Deadline: {}ms\n",
task.name, task.deadline_ms
));
self.indent_level += 1;
self.gen_block_cpp(&task.body)?;
self.indent_level -= 1;
self.indent();
self.code.push_str("}\n");
}
self.code.push_str("};\n\n");
}
Item::Function(func) => {
self.gen_fn_cpp(func)?;
}
Item::Task(task) => {
self.code.push_str(&format!("void task_{}() {{\n", task.name));
self.indent_level = 1;
self.gen_block_cpp(&task.body)?;
self.code.push_str("}\n\n");
}
_ => {}
}
}
self.code.push_str("int main() {\n std::cout << \"=== Sesh 1.0.0 Bare-Metal Firmware Started ===\\n\";\n main_sesh();\n return 0;\n}\n");
Ok(self.code.clone())
}
fn gen_fn_cpp(&mut self, func: &FnDecl) -> Result<(), CodegenError> {
self.indent();
let name = if func.name == "main" { "main_sesh" } else { &func.name };
let ret = if func.ret_type == Type::Void { "void" } else { "double" };
self.code.push_str(&format!("{} {}() {{\n", ret, name));
self.indent_level += 1;
self.gen_block_cpp(&func.body)?;
self.indent_level -= 1;
self.indent();
self.code.push_str("}\n\n");
Ok(())
}
fn gen_block_cpp(&mut self, block: &Block) -> Result<(), CodegenError> {
for stmt in &block.statements {
self.gen_stmt_cpp(stmt)?;
}
Ok(())
}
fn gen_stmt_cpp(&mut self, stmt: &Stmt) -> Result<(), CodegenError> {
self.indent();
match stmt {
Stmt::Let { name, init, .. } => {
let val_str = self.gen_expr_cpp(init);
self.code.push_str(&format!("auto {} = {};\n", name, val_str));
}
Stmt::Assign { target, value, .. } => {
let t_str = self.gen_expr_cpp(target);
let v_str = self.gen_expr_cpp(value);
self.code.push_str(&format!("{} = {};\n", t_str, v_str));
}
Stmt::If { cond, then_block, else_block, .. } => {
let c_str = self.gen_expr_cpp(cond);
self.code.push_str(&format!("if ({}) {{\n", c_str));
self.indent_level += 1;
self.gen_block_cpp(then_block)?;
self.indent_level -= 1;
self.indent();
self.code.push_str("}");
if let Some(eb) = else_block {
self.code.push_str(" else {\n");
self.indent_level += 1;
self.gen_block_cpp(eb)?;
self.indent_level -= 1;
self.indent();
self.code.push_str("}\n");
} else {
self.code.push('\n');
}
}
Stmt::While { cond, body, .. } => {
let c_str = self.gen_expr_cpp(cond);
self.code.push_str(&format!("while ({}) {{\n", c_str));
self.indent_level += 1;
self.gen_block_cpp(body)?;
self.indent_level -= 1;
self.indent();
self.code.push_str("}\n");
}
Stmt::For { var, start, end, body, .. } => {
let s_str = self.gen_expr_cpp(start);
let e_str = self.gen_expr_cpp(end);
self.code.push_str(&format!(
"for (int {} = {}; {} < {}; ++{}) {{\n",
var, s_str, var, e_str, var
));
self.indent_level += 1;
self.gen_block_cpp(body)?;
self.indent_level -= 1;
self.indent();
self.code.push_str("}\n");
}
Stmt::Loop { body, .. } => {
self.code.push_str("while (true) {\n");
self.indent_level += 1;
self.gen_block_cpp(body)?;
self.indent_level -= 1;
self.indent();
self.code.push_str("}\n");
}
Stmt::Break(_) => self.code.push_str("break;\n"),
Stmt::Continue(_) => self.code.push_str("continue;\n"),
Stmt::Return { value, .. } => {
if let Some(v) = value {
let v_str = self.gen_expr_cpp(v);
self.code.push_str(&format!("return {};\n", v_str));
} else {
self.code.push_str("return;\n");
}
}
Stmt::Emit { topic, payload, .. } => {
let p_str = self.gen_expr_cpp(payload);
self.code.push_str(&format!(
"sesh_rt::ros_publish(\"{}\", std::to_string({}));\n",
topic, p_str
));
}
Stmt::Expr(expr) => {
let e_str = self.gen_expr_cpp(expr);
self.code.push_str(&format!("{};\n", e_str));
}
_ => {
self.code.push_str("// statement\n");
}
}
Ok(())
}
fn gen_expr_cpp(&self, expr: &Expr) -> String {
match expr {
Expr::Literal(lit, _) => match lit {
Literal::Int(v) => v.to_string(),
Literal::Float(v) => format!("{:.2}", v),
Literal::Bool(b) => if *b { "true".to_string() } else { "false".to_string() },
Literal::String(s) => format!("\"{}\"", s),
Literal::DurationMs(ms) => ms.to_string(),
Literal::HardwareConstant(s) => match s.as_str() {
"HIGH" | "OUT" | "RISING" => "1".to_string(),
_ => "0".to_string(),
},
},
Expr::Variable(name, _) => name.clone(),
Expr::FieldAccess(recv, f, _) => {
let r_str = self.gen_expr_cpp(recv);
if r_str == "self" {
format!("pin_{}", f)
} else {
format!("{}.{}", r_str, f)
}
}
Expr::Binary { op, left, right, .. } => {
let op_str = match op {
BinaryOp::Add => "+",
BinaryOp::Sub => "-",
BinaryOp::Mul => "*",
BinaryOp::Div => "/",
BinaryOp::Mod => "%",
BinaryOp::Eq => "==",
BinaryOp::NotEq => "!=",
BinaryOp::Lt => "<",
BinaryOp::LtEq => "<=",
BinaryOp::Gt => ">",
BinaryOp::GtEq => ">=",
BinaryOp::And => "&&",
BinaryOp::Or => "||",
BinaryOp::BitAnd => "&",
BinaryOp::BitOr => "|",
BinaryOp::BitXor => "^",
BinaryOp::Shl => "<<",
BinaryOp::Shr => ">>",
};
format!("({} {} {})", self.gen_expr_cpp(left), op_str, self.gen_expr_cpp(right))
}
Expr::Unary { op, operand, .. } => {
let op_str = match op {
UnaryOp::Neg => "-",
UnaryOp::Not => "!",
UnaryOp::BitNot => "~",
};
format!("({}{})", op_str, self.gen_expr_cpp(operand))
}
Expr::Call { func, args, .. } => {
let args_str = args.iter().map(|a| self.gen_expr_cpp(a)).collect::<Vec<_>>().join(", ");
let cpp_func = match func.as_str() {
"std::gpio::write" => "sesh_rt::gpio_write",
"std::pwm::write" => "sesh_rt::pwm_write",
"std::sensor::ultrasonic_read" => "sesh_rt::ultrasonic_read",
"std::ros::init" => "sesh_rt::ros_init",
"std::ros::publish" => "sesh_rt::ros_publish",
"std::motor::pid_compute" => "sesh_rt::pid_compute",
"std::servo::write_angle" => "sesh_rt::servo_write_angle",
"std::stepper::set_microsteps" => "sesh_rt::stepper_set_microsteps",
"std::stepper::step_angle" => "sesh_rt::stepper_step_angle",
"std::watchdog::start" => "sesh_rt::watchdog_start",
"std::watchdog::reset" => "sesh_rt::watchdog_reset",
"std::kinematics::ik_3dof_arm" => "sesh_rt::kinematics_ik_3dof_arm",
"std::kinematics::diff_drive_ik" => "sesh_rt::kinematics_diff_drive_ik",
"std::kalman::ekf_update" => "sesh_rt::kalman_ekf_update",
"std::kalman::fuse_imu_gps" => "sesh_rt::kalman_fuse_imu_gps",
"std::control::lqr_compute" => "sesh_rt::control_lqr_compute",
"std::gps::distance_to_waypoint" => "sesh_rt::gps_distance_to_waypoint",
"std::lidar::scan_360" => "sesh_rt::lidar_scan_360",
"std::lidar::find_closest_obstacle" => "sesh_rt::lidar_find_closest_obstacle",
"std::camera::capture" => "sesh_rt::camera_capture",
"std::ai::classify_obstacle" => "sesh_rt::ai_classify_obstacle",
"print" | "println" => "std::cout << ",
other => other,
};
if cpp_func == "std::cout << " {
format!("std::cout << {} << \"\\n\"", args_str)
} else {
format!("{}({})", cpp_func, args_str)
}
}
Expr::MethodCall { receiver, method, args, .. } => {
let args_str = args.iter().map(|a| self.gen_expr_cpp(a)).collect::<Vec<_>>().join(", ");
format!("{}.{}({})", self.gen_expr_cpp(receiver), method, args_str)
}
_ => "0".to_string(),
}
}
pub fn compile_to_binary(&mut self, program: &Program, output_path: &Path, opt_level: &str) -> Result<(), CodegenError> {
let cpp_code = self.generate_cpp(program)?;
let temp_cpp_path = output_path.with_extension("cpp");
if let Some(parent) = output_path.parent() {
if !parent.as_os_str().is_empty() {
std::fs::create_dir_all(parent)?;
}
}
let mut file = File::create(&temp_cpp_path)?;
file.write_all(cpp_code.as_bytes())?;
let opt_flag = format!("-{}", opt_level);
let output = Command::new("g++")
.arg("-std=c++20")
.arg(&opt_flag)
.arg(&temp_cpp_path)
.arg("-o")
.arg(output_path)
.output()
.map_err(|e| CodegenError::LinkerError(e.to_string()))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(CodegenError::LinkerError(format!("G++ Bare-Metal compile error: {}", stderr)));
}
let _ = std::fs::remove_file(&temp_cpp_path);
Ok(())
}
}
pub struct Ros2CppCodegen {
code: String,
}
impl Ros2CppCodegen {
pub fn new() -> Self {
Self {
code: String::new(),
}
}
pub fn generate_node(&mut self, program: &Program) -> Result<String, CodegenError> {
self.code.clear();
self.code.push_str("// ============================================================================\n");
self.code.push_str("// AUTO-GENERATED BY SESH 1.0.0 COMPILER (ROS 2 C++20 Node Target)\n");
self.code.push_str("// ============================================================================\n\n");
self.code.push_str("#include <chrono>\n#include <memory>\n#include <string>\n#include <iostream>\n");
self.code.push_str("// Sesh ROS 2 C++ Runtime Bridge\nnamespace sesh_ros2 {\n");
self.code.push_str(" inline void log_info(const std::string& msg) { std::cout << \"[ROS2:INFO] \" << msg << \"\\n\"; }\n");
self.code.push_str(" struct node_base { virtual ~node_base() = default; };\n");
self.code.push_str("}\n\n");
self.code.push_str("namespace sesh_rt {\n");
self.code.push_str(" inline void gpio_write(uint32_t pin, bool level) { std::cout << \"[HW:GPIO] Pin \" << pin << \" -> \" << (level ? \"HIGH\" : \"LOW\") << \"\\n\"; }\n");
self.code.push_str(" inline void pwm_write(uint32_t pin, uint32_t duty) { std::cout << \"[HW:PWM] Pin \" << pin << \" -> Duty \" << duty << \"%\\n\"; }\n");
self.code.push_str(" inline void ros_publish(const std::string& topic, const std::string& msg) { std::cout << \"[ROS2:PUB] /\" << topic << \" : \" << msg << \"\\n\"; }\n");
self.code.push_str(" inline void ros_init(const std::string& name) { std::cout << \"[ROS2:INIT] \" << name << \"\\n\"; }\n");
self.code.push_str(" inline int camera_capture() { return 0; }\n");
self.code.push_str(" inline std::string ai_classify_obstacle(double d, double c) { return \"Person\"; }\n");
self.code.push_str("}\n\n");
for item in &program.items {
if let Item::Robot(robot) = item {
let mut helper = BaremetalCppCodegen::new();
self.code.push_str(&format!("class {}Node : public sesh_ros2::node_base {{\npublic:\n", robot.name));
for pin in &robot.pins {
self.code.push_str(&format!(
" static constexpr uint32_t pin_{} = {};\n",
pin.name, pin.pin_num
));
}
self.code.push_str(&format!(" {}Node() {{\n sesh_ros2::log_info(\"Initializing ROS 2 Node {}\");\n }}\n", robot.name, robot.name));
for method in &robot.methods {
let _ = helper.gen_fn_cpp(method);
self.code.push_str(&helper.code);
helper.code.clear();
}
for task in &robot.tasks {
self.code.push_str(&format!(" void timer_callback_{}() {{\n", task.name));
self.code.push_str(&format!(" // Real-Time ROS 2 Timer Loop (Deadline: {} ms)\n", task.deadline_ms));
helper.indent_level = 2;
let _ = helper.gen_block_cpp(&task.body);
self.code.push_str(&helper.code);
helper.code.clear();
self.code.push_str(" }\n");
}
self.code.push_str("};\n\n");
}
}
self.code.push_str("int main(int argc, char** argv) {\n std::cout << \"=== Sesh 1.0.0 ROS 2 C++ Node Started ===\\n\";\n return 0;\n}\n");
Ok(self.code.clone())
}
pub fn compile_to_binary(&mut self, program: &Program, output_path: &Path, opt_level: &str) -> Result<(), CodegenError> {
let cpp_code = self.generate_node(program)?;
let temp_cpp_path = output_path.with_extension("cpp");
if let Some(parent) = output_path.parent() {
if !parent.as_os_str().is_empty() {
std::fs::create_dir_all(parent)?;
}
}
let mut file = File::create(&temp_cpp_path)?;
file.write_all(cpp_code.as_bytes())?;
let opt_flag = format!("-{}", opt_level);
let output = Command::new("g++")
.arg("-std=c++20")
.arg(&opt_flag)
.arg(&temp_cpp_path)
.arg("-o")
.arg(output_path)
.output()
.map_err(|e| CodegenError::LinkerError(e.to_string()))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(CodegenError::LinkerError(format!("G++ ROS2 compile error: {}", stderr)));
}
let _ = std::fs::remove_file(&temp_cpp_path);
Ok(())
}
}
#[allow(dead_code)]
pub struct AArch64Codegen {
asm: String,
string_literals: Vec<(String, String)>,
label_counter: usize,
}
impl AArch64Codegen {
pub fn new() -> Self {
Self {
asm: String::new(),
string_literals: Vec::new(),
label_counter: 0,
}
}
pub fn generate_asm(&mut self, _program: &Program) -> Result<String, CodegenError> {
self.asm.clear();
self.asm.push_str(".arch armv8-a\n");
self.asm.push_str(".text\n");
self.asm.push_str(".globl main\n");
self.asm.push_str(".type main, %function\n\n");
self.asm.push_str("main:\n");
self.asm.push_str(" stp x29, x30, [sp, -32]!\n");
self.asm.push_str(" mov x29, sp\n");
self.asm.push_str(" # Sesh ARM64 Runtime Initialization\n");
self.asm.push_str(" mov w0, #0\n");
self.asm.push_str(" ldp x29, x30, [sp], 32\n");
self.asm.push_str(" ret\n\n");
Ok(self.asm.clone())
}
pub fn compile_to_native(
&mut self,
program: &Program,
output_path: &Path,
opt_level: &str,
) -> Result<(), CodegenError> {
let asm_code = self.generate_asm(program)?;
let temp_asm_path = output_path.with_extension("s");
let temp_c_path = output_path.with_extension("c");
if let Some(parent) = output_path.parent() {
if !parent.as_os_str().is_empty() {
std::fs::create_dir_all(parent)?;
}
}
let mut file = File::create(&temp_asm_path)?;
file.write_all(asm_code.as_bytes())?;
let c_stub = r#"#include <stdio.h>
#include <stdint.h>
void sesh_rt_arm64_init() { printf("[ARM64:HAL] Sesh Cortex-A hardware initialized.\n"); }
"#;
let mut c_file = File::create(&temp_c_path)?;
c_file.write_all(c_stub.as_bytes())?;
let has_aarch64_gcc = Command::new("aarch64-linux-gnu-gcc").arg("--version").output().is_ok();
let is_native_arm = std::env::consts::ARCH == "aarch64";
let compiler = if has_aarch64_gcc {
"aarch64-linux-gnu-gcc"
} else if is_native_arm {
"gcc"
} else {
return Err(CodegenError::LinkerError(
"ARM64 cross-compilation requires 'aarch64-linux-gnu-gcc' to assemble ELF binary. ARM64 assembly '.s' generated successfully.".to_string(),
));
};
let opt_flag = format!("-{}", opt_level);
let output = Command::new(compiler)
.arg("-no-pie")
.arg(&opt_flag)
.arg(&temp_asm_path)
.arg(&temp_c_path)
.arg("-o")
.arg(output_path)
.output()
.map_err(|e| CodegenError::LinkerError(e.to_string()))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(CodegenError::LinkerError(format!("ARM64 compiler error: {}", stderr)));
}
let _ = std::fs::remove_file(&temp_asm_path);
let _ = std::fs::remove_file(&temp_c_path);
Ok(())
}
}
pub struct X86_64Codegen {
asm: String,
string_literals: Vec<(String, String)>,
label_counter: usize,
var_offsets: HashMap<String, isize>,
current_stack_offset: isize,
loop_stack: Vec<(String, String)>, }
impl X86_64Codegen {
pub fn new() -> Self {
Self {
asm: String::new(),
string_literals: Vec::new(),
label_counter: 0,
var_offsets: HashMap::new(),
current_stack_offset: -8,
loop_stack: Vec::new(),
}
}
fn next_label(&mut self, prefix: &str) -> String {
let label = format!(".L_{}_{}", prefix, self.label_counter);
self.label_counter += 1;
label
}
pub fn generate_asm(&mut self, program: &Program) -> Result<String, CodegenError> {
self.asm.clear();
self.asm.push_str(".intel_syntax noprefix\n");
self.asm.push_str(".text\n");
self.asm.push_str(".globl main\n");
self.asm.push_str(".type main, @function\n\n");
for item in &program.items {
match item {
Item::Function(func) => {
self.gen_function(func)?;
}
Item::Robot(robot) => {
for method in &robot.methods {
let mut m = method.clone();
m.name = format!("{}_{}", robot.name, method.name);
self.gen_function(&m)?;
}
for task in &robot.tasks {
self.gen_task(&robot.name, task)?;
}
}
Item::Task(task) => {
self.gen_task("global", task)?;
}
_ => {}
}
}
self.asm.push_str("main:\n");
self.asm.push_str(" push rbp\n");
self.asm.push_str(" mov rbp, rsp\n");
let has_main = program.items.iter().any(|item| {
if let Item::Function(f) = item {
f.name == "main"
} else {
false
}
});
if has_main {
self.asm.push_str(" call main_sesh\n");
} else {
self.asm.push_str(" mov eax, 0\n");
}
self.asm.push_str(" mov eax, 0\n");
self.asm.push_str(" pop rbp\n");
self.asm.push_str(" ret\n\n");
if !self.string_literals.is_empty() {
self.asm.push_str(".section .rodata\n");
for (label, text) in &self.string_literals {
self.asm.push_str(&format!("{}:\n", label));
self.asm.push_str(&format!(" .string \"{}\"\n", text.replace('"', "\\\"")));
}
}
Ok(self.asm.clone())
}
fn gen_function(&mut self, func: &FnDecl) -> Result<(), CodegenError> {
let fn_label = if func.name == "main" {
"main_sesh".to_string()
} else {
func.name.clone()
};
self.asm.push_str(&format!("{}:\n", fn_label));
self.asm.push_str(" push rbp\n");
self.asm.push_str(" mov rbp, rsp\n");
self.asm.push_str(" sub rsp, 256\n");
self.var_offsets.clear();
self.current_stack_offset = -8;
let arg_regs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
for (i, param) in func.params.iter().enumerate() {
if i < arg_regs.len() {
let offset = self.current_stack_offset;
self.var_offsets.insert(param.name.clone(), offset);
self.asm.push_str(&format!(" mov [rbp + {}], {}\n", offset, arg_regs[i]));
self.current_stack_offset -= 8;
}
}
self.gen_block(&func.body)?;
self.asm.push_str(" mov rsp, rbp\n");
self.asm.push_str(" pop rbp\n");
self.asm.push_str(" ret\n\n");
Ok(())
}
fn gen_task(&mut self, owner: &str, task: &TaskDecl) -> Result<(), CodegenError> {
let label = format!("{}_task_{}", owner, task.name);
self.asm.push_str(&format!("{}:\n", label));
self.asm.push_str(" push rbp\n");
self.asm.push_str(" mov rbp, rsp\n");
self.asm.push_str(" sub rsp, 256\n");
self.var_offsets.clear();
self.current_stack_offset = -8;
self.gen_block(&task.body)?;
self.asm.push_str(" mov rsp, rbp\n");
self.asm.push_str(" pop rbp\n");
self.asm.push_str(" ret\n\n");
Ok(())
}
fn gen_block(&mut self, block: &Block) -> Result<(), CodegenError> {
for stmt in &block.statements {
self.gen_stmt(stmt)?;
}
Ok(())
}
fn gen_stmt(&mut self, stmt: &Stmt) -> Result<(), CodegenError> {
match stmt {
Stmt::Let { name, init, .. } => {
self.gen_expr(init)?;
let offset = self.current_stack_offset;
self.var_offsets.insert(name.clone(), offset);
self.asm.push_str(&format!(" mov [rbp + {}], rax\n", offset));
self.current_stack_offset -= 8;
}
Stmt::Assign { target, value, .. } => {
self.gen_expr(value)?;
if let Expr::Variable(name, _) = target {
if let Some(&offset) = self.var_offsets.get(name) {
self.asm.push_str(&format!(" mov [rbp + {}], rax\n", offset));
}
}
}
Stmt::If { cond, then_block, else_block, .. } => {
let lbl_else = self.next_label("if_else");
let lbl_end = self.next_label("if_end");
self.gen_expr(cond)?;
self.asm.push_str(" cmp rax, 0\n");
self.asm.push_str(&format!(" je {}\n", lbl_else));
self.gen_block(then_block)?;
self.asm.push_str(&format!(" jmp {}\n", lbl_end));
self.asm.push_str(&format!("{}:\n", lbl_else));
if let Some(eb) = else_block {
self.gen_block(eb)?;
}
self.asm.push_str(&format!("{}:\n", lbl_end));
}
Stmt::While { cond, body, .. } => {
let lbl_cond = self.next_label("while_cond");
let lbl_end = self.next_label("while_end");
self.loop_stack.push((lbl_cond.clone(), lbl_end.clone()));
self.asm.push_str(&format!("{}:\n", lbl_cond));
self.gen_expr(cond)?;
self.asm.push_str(" cmp rax, 0\n");
self.asm.push_str(&format!(" je {}\n", lbl_end));
self.gen_block(body)?;
self.asm.push_str(&format!(" jmp {}\n", lbl_cond));
self.asm.push_str(&format!("{}:\n", lbl_end));
self.loop_stack.pop();
}
Stmt::For { start, end, body, .. } => {
let lbl_cond = self.next_label("for_cond");
let lbl_end = self.next_label("for_end");
self.loop_stack.push((lbl_cond.clone(), lbl_end.clone()));
self.gen_expr(start)?;
self.asm.push_str(" mov r12, rax\n");
self.gen_expr(end)?;
self.asm.push_str(" mov r13, rax\n");
self.asm.push_str(&format!("{}:\n", lbl_cond));
self.asm.push_str(" cmp r12, r13\n");
self.asm.push_str(&format!(" jge {}\n", lbl_end));
self.gen_block(body)?;
self.asm.push_str(" inc r12\n");
self.asm.push_str(&format!(" jmp {}\n", lbl_cond));
self.asm.push_str(&format!("{}:\n", lbl_end));
self.loop_stack.pop();
}
Stmt::Loop { body, .. } => {
let lbl_start = self.next_label("loop_start");
let lbl_end = self.next_label("loop_end");
self.loop_stack.push((lbl_start.clone(), lbl_end.clone()));
self.asm.push_str(&format!("{}:\n", lbl_start));
self.gen_block(body)?;
self.asm.push_str(&format!(" jmp {}\n", lbl_start));
self.asm.push_str(&format!("{}:\n", lbl_end));
self.loop_stack.pop();
}
Stmt::Break(_) => {
if let Some((_, end_lbl)) = self.loop_stack.last() {
self.asm.push_str(&format!(" jmp {}\n", end_lbl));
}
}
Stmt::Continue(_) => {
if let Some((cond_lbl, _)) = self.loop_stack.last() {
self.asm.push_str(&format!(" jmp {}\n", cond_lbl));
}
}
Stmt::Return { value, .. } => {
if let Some(expr) = value {
self.gen_expr(expr)?;
} else {
self.asm.push_str(" mov rax, 0\n");
}
self.asm.push_str(" mov rsp, rbp\n");
self.asm.push_str(" pop rbp\n");
self.asm.push_str(" ret\n");
}
Stmt::Expr(expr) => {
self.gen_expr(expr)?;
}
_ => {
self.asm.push_str(" # statement\n");
}
}
Ok(())
}
fn is_string_expr(expr: &Expr) -> bool {
match expr {
Expr::Literal(Literal::String(_), _) => true,
Expr::Binary { op: BinaryOp::Add, left, right, .. } => {
Self::is_string_expr(left) || Self::is_string_expr(right)
}
_ => false,
}
}
fn get_expr_type_tag(expr: &Expr) -> u32 {
match expr {
Expr::Literal(Literal::String(_), _) => 2,
Expr::Literal(Literal::Float(_), _) => 1,
Expr::Binary { op: BinaryOp::Add, left, right, .. } => {
if Self::is_string_expr(left) || Self::is_string_expr(right) {
2
} else if Self::get_expr_type_tag(left) == 1 || Self::get_expr_type_tag(right) == 1 {
1
} else {
0
}
}
Expr::Call { func, .. } => {
if matches!(
func.as_str(),
"std::motor::pid_compute"
| "std::kalman::ekf_update"
| "std::kalman::fuse_imu_gps"
| "std::kinematics::ik_3dof_arm"
| "std::control::lqr_compute"
| "std::gps::distance_to_waypoint"
| "std::sensor::ultrasonic_read"
) {
1
} else {
0
}
}
_ => 0,
}
}
fn gen_expr(&mut self, expr: &Expr) -> Result<(), CodegenError> {
match expr {
Expr::Literal(lit, _) => {
match lit {
Literal::Int(v) => {
self.asm.push_str(&format!(" mov rax, {}\n", v));
}
Literal::Float(v) => {
let bits = v.to_bits();
self.asm.push_str(&format!(" mov rax, {}\n", bits));
}
Literal::Bool(b) => {
let val = if *b { 1 } else { 0 };
self.asm.push_str(&format!(" mov rax, {}\n", val));
}
Literal::HardwareConstant(s) => {
let val = match s.as_str() {
"HIGH" | "OUT" | "RISING" => 1,
_ => 0,
};
self.asm.push_str(&format!(" mov rax, {}\n", val));
}
Literal::String(s) => {
let label = self.next_label("str");
self.string_literals.push((label.clone(), s.clone()));
self.asm.push_str(&format!(" lea rax, [rip + {}]\n", label));
}
_ => {
self.asm.push_str(" mov rax, 0\n");
}
}
}
Expr::Variable(name, _) => {
if let Some(&offset) = self.var_offsets.get(name) {
self.asm.push_str(&format!(" mov rax, [rbp + {}]\n", offset));
} else {
self.asm.push_str(" mov rax, 0\n");
}
}
Expr::Binary { op, left, right, .. } => {
if *op == BinaryOp::Add && (Self::is_string_expr(left) || Self::is_string_expr(right)) {
let type_l = Self::get_expr_type_tag(left);
let type_r = Self::get_expr_type_tag(right);
self.gen_expr(left)?;
self.asm.push_str(" mov rdi, rax\n push rdi\n");
self.gen_expr(right)?;
self.asm.push_str(" mov rsi, rax\n pop rdi\n");
self.asm.push_str(&format!(" mov edx, {}\n mov ecx, {}\n", type_l, type_r));
self.asm.push_str(" call sesh_rt_str_concat\n");
return Ok(());
}
self.gen_expr(left)?;
self.asm.push_str(" push rax\n");
self.gen_expr(right)?;
self.asm.push_str(" pop rbx\n");
match op {
BinaryOp::Add => self.asm.push_str(" add rax, rbx\n"),
BinaryOp::Sub => {
self.asm.push_str(" xchg rax, rbx\n");
self.asm.push_str(" sub rax, rbx\n");
}
BinaryOp::Mul => self.asm.push_str(" imul rax, rbx\n"),
BinaryOp::Lt => {
self.asm.push_str(" cmp rbx, rax\n");
self.asm.push_str(" setl al\n movzx rax, al\n");
}
BinaryOp::Gt => {
self.asm.push_str(" cmp rbx, rax\n");
self.asm.push_str(" setg al\n movzx rax, al\n");
}
BinaryOp::Eq => {
self.asm.push_str(" cmp rbx, rax\n");
self.asm.push_str(" sete al\n movzx rax, al\n");
}
_ => {}
}
}
Expr::Call { func, args, .. } => {
let is_float_call = matches!(
func.as_str(),
"std::motor::pid_compute"
| "std::kalman::ekf_update"
| "std::kalman::fuse_imu_gps"
| "std::kinematics::ik_3dof_arm"
| "std::control::lqr_compute"
| "std::gps::distance_to_waypoint"
);
if is_float_call {
for (i, arg) in args.iter().enumerate() {
if i < 8 {
self.gen_expr(arg)?;
self.asm.push_str(&format!(" movq xmm{}, rax\n", i));
}
}
} else {
let regs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
for (i, arg) in args.iter().enumerate() {
if i < regs.len() {
self.gen_expr(arg)?;
self.asm.push_str(&format!(" mov {}, rax\n", regs[i]));
}
}
}
let mut is_str_print = false;
if (func == "print" || func == "println") && !args.is_empty() {
if Self::is_string_expr(&args[0]) {
is_str_print = true;
}
}
let sanitized;
let sym = match func.as_str() {
"std::gpio::write" => "sesh_rt_gpio_write",
"std::pwm::write" => "sesh_rt_pwm_write",
"std::sensor::ultrasonic_read" => "sesh_rt_ultrasonic_read",
"std::motor::pid_compute" => "sesh_rt_pid_compute",
"std::ros::init" => "sesh_rt_ros_init",
"std::ros::publish" => "sesh_rt_ros_publish",
"print" => if is_str_print { "sesh_rt_print_str" } else { "sesh_rt_print_int" },
"println" => if is_str_print { "sesh_rt_println_str" } else { "sesh_rt_println_int" },
other => {
sanitized = other.replace("::", "_").replace('.', "_");
&sanitized
}
};
self.asm.push_str(&format!(" call {}\n", sym));
if is_float_call {
self.asm.push_str(" movq rax, xmm0\n");
}
}
Expr::MethodCall { method, args, .. } => {
let regs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
for (i, arg) in args.iter().enumerate() {
if i < regs.len() {
self.gen_expr(arg)?;
self.asm.push_str(&format!(" mov {}, rax\n", regs[i]));
}
}
let sanitized = method.replace("::", "_").replace('.', "_");
self.asm.push_str(&format!(" call {}\n", sanitized));
}
_ => {
self.asm.push_str(" mov rax, 0\n");
}
}
Ok(())
}
pub fn compile_to_native(
&mut self,
program: &Program,
output_path: &Path,
opt_level: &str,
) -> Result<(), CodegenError> {
let asm_code = self.generate_asm(program)?;
let temp_asm_path = output_path.with_extension("s");
let temp_c_path = output_path.with_extension("c");
if let Some(parent) = output_path.parent() {
if !parent.as_os_str().is_empty() {
std::fs::create_dir_all(parent)?;
}
}
let mut file = File::create(&temp_asm_path)?;
file.write_all(asm_code.as_bytes())?;
let c_stub = r#"#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
void sesh_rt_gpio_write(uint32_t pin, uint64_t level) {
printf("[HW:GPIO] Pin %u -> %s\n", pin, level ? "HIGH" : "LOW");
}
void sesh_rt_pwm_write(uint32_t pin, uint32_t duty) {
printf("[HW:PWM] Pin %u -> Duty %u%%\n", pin, duty);
}
void sesh_rt_ros_init(const char* node) {
printf("[ROS2:INIT] Node -> %s\n", node ? node : "default");
}
void sesh_rt_ros_publish(const char* topic, const char* msg) {
printf("[ROS2:PUB] /%s : %s\n", topic ? topic : "null", msg ? msg : "null");
}
double sesh_rt_pid_compute(double sp, double act, double kp, double ki, double kd) {
static double integral = 0.0;
static double prev_error = 0.0;
double dt = 0.005;
double err = sp - act;
integral += err * dt;
if (integral > 100.0) integral = 100.0;
if (integral < -100.0) integral = -100.0;
double deriv = (err - prev_error) / dt;
prev_error = err;
double out = (kp * err) + (ki * integral) + (kd * deriv);
if (out > 100.0) out = 100.0;
if (out < -100.0) out = -100.0;
return out;
}
char* sesh_rt_str_concat(int64_t a, int64_t b, int type_a, int type_b) {
static char buf[2048];
char str_a[512];
char str_b[512];
if (type_a == 2) {
snprintf(str_a, sizeof(str_a), "%s", (const char*)a ? (const char*)a : "");
} else if (type_a == 1) {
double af = *(double*)&a;
snprintf(str_a, sizeof(str_a), "%.2f", af);
} else {
snprintf(str_a, sizeof(str_a), "%ld", (long)a);
}
if (type_b == 2) {
snprintf(str_b, sizeof(str_b), "%s", (const char*)b ? (const char*)b : "");
} else if (type_b == 1) {
double bf = *(double*)&b;
snprintf(str_b, sizeof(str_b), "%.2f", bf);
} else {
snprintf(str_b, sizeof(str_b), "%ld", (long)b);
}
snprintf(buf, sizeof(buf), "%s%s", str_a, str_b);
return buf;
}
double sesh_rt_ultrasonic_read(uint32_t trig, uint32_t echo) {
return 45.0;
}
void sesh_rt_print_str(const char* s) {
printf("%s", s ? s : "");
}
void sesh_rt_print_int(int64_t val) {
printf("%ld", (long)val);
}
void sesh_rt_println_str(const char* s) {
printf("%s\n", s ? s : "");
}
void sesh_rt_println_int(int64_t val) {
printf("%ld\n", (long)val);
}
"#;
let mut c_file = File::create(&temp_c_path)?;
c_file.write_all(c_stub.as_bytes())?;
let opt_flag = format!("-{}", opt_level);
let output = Command::new("gcc")
.arg("-no-pie")
.arg(&opt_flag)
.arg(&temp_asm_path)
.arg(&temp_c_path)
.arg("-o")
.arg(output_path)
.arg("-lm")
.output()
.map_err(|e| CodegenError::LinkerError(e.to_string()))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let output_retry = Command::new("gcc")
.arg(&opt_flag)
.arg(&temp_asm_path)
.arg(&temp_c_path)
.arg("-o")
.arg(output_path)
.arg("-lm")
.output()
.map_err(|e| CodegenError::LinkerError(e.to_string()))?;
if !output_retry.status.success() {
return Err(CodegenError::LinkerError(format!(
"GCC Linker error: {}\nRetry error: {}",
stderr,
String::from_utf8_lossy(&output_retry.stderr)
)));
}
}
let _ = std::fs::remove_file(&temp_asm_path);
let _ = std::fs::remove_file(&temp_c_path);
Ok(())
}
}
pub fn compile_program(
program: &Program,
target: TargetArch,
output_path: &Path,
opt_level: &str,
) -> Result<(), CodegenError> {
match target {
TargetArch::BaremetalCpp => {
let mut codegen = BaremetalCppCodegen::new();
codegen.compile_to_binary(program, output_path, opt_level)
}
TargetArch::Ros2Cpp => {
let mut codegen = Ros2CppCodegen::new();
codegen.compile_to_binary(program, output_path, opt_level)
}
TargetArch::AArch64Linux => {
let mut codegen = AArch64Codegen::new();
codegen.compile_to_native(program, output_path, opt_level)
}
TargetArch::X86_64Linux => {
let mut codegen = X86_64Codegen::new();
codegen.compile_to_native(program, output_path, opt_level)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_asm_generation() {
let mut codegen = X86_64Codegen::new();
let program = Program {
imports: vec![],
items: vec![],
};
let asm = codegen.generate_asm(&program).unwrap();
assert!(asm.contains(".intel_syntax noprefix"));
assert!(asm.contains("main:"));
}
#[test]
fn test_cpp_generation() {
let mut codegen = BaremetalCppCodegen::new();
let program = Program {
imports: vec![],
items: vec![],
};
let cpp = codegen.generate_cpp(&program).unwrap();
assert!(cpp.contains("#include <cstdint>"));
assert!(cpp.contains("namespace sesh_rt"));
}
}