use syn::{Expr, Local, BinOp};
use super::{Compiler, CompileError, VarLocation, VarType};
impl Compiler {
pub(crate) fn compile_local(&mut self, local: &Local) -> Result<(), CompileError> {
if let Some(init) = &local.init {
self.compile_expr(&init.expr)?;
let name = Self::extract_pat_name(&local.pat)?;
let (var_type, is_signed) = self.detect_full_type(&local.pat, &init.expr);
let reg = self.define_var(&name, var_type, is_signed)?;
self.emit_pop_reg(reg);
} else {
return Err(CompileError("Uninitialized let bindings not supported".to_string()));
}
Ok(())
}
fn detect_full_type(&self, pat: &syn::Pat, init: &Expr) -> (VarType, bool) {
if let syn::Pat::Type(pat_type) = pat {
if let syn::Type::Path(type_path) = &*pat_type.ty {
if let Some(segment) = type_path.path.segments.last() {
let type_name = segment.ident.to_string();
return match type_name.as_str() {
"i8" | "i16" | "i32" | "i64" | "isize" => (VarType::Integer, true),
"u8" | "u16" | "u32" | "u64" | "usize" => (VarType::Integer, false),
"bool" => (VarType::Bool, false),
"String" => (VarType::String, false),
"Vec" => (VarType::Vector, false),
_ => self.infer_type_from_expr(init),
};
}
}
}
self.infer_type_from_expr(init)
}
fn infer_type_from_expr(&self, expr: &Expr) -> (VarType, bool) {
match expr {
Expr::Lit(lit) => {
match &lit.lit {
syn::Lit::Str(_) | syn::Lit::ByteStr(_) => (VarType::String, false),
syn::Lit::Bool(_) => (VarType::Bool, false),
syn::Lit::Int(i) => {
let suffix = i.suffix();
let is_signed = suffix.starts_with('i');
(VarType::Integer, is_signed)
}
_ => (VarType::Integer, false),
}
}
Expr::Array(_) | Expr::Repeat(_) => (VarType::Vector, false),
Expr::MethodCall(mc) => {
let method = mc.method.to_string();
if matches!(method.as_str(), "concat" | "to_string") {
(VarType::String, false)
} else {
if let Some(var_type) = self.infer_method_result_type(mc) {
(var_type, false)
} else {
(VarType::Integer, false)
}
}
}
Expr::Path(path) => {
if path.path.segments.len() == 1 {
let name = path.path.segments[0].ident.to_string();
if let Some(var_type) = self.get_var_type(&name) {
let is_signed = self.is_var_signed(&name);
(var_type, is_signed)
} else {
(VarType::Integer, false)
}
} else {
(VarType::Integer, false)
}
}
Expr::Unary(unary) => {
if matches!(unary.op, syn::UnOp::Neg(_)) {
(VarType::Integer, true)
} else {
self.infer_type_from_expr(&unary.expr)
}
}
Expr::Binary(binary) => {
self.infer_type_from_expr(&binary.left)
}
_ => (VarType::Integer, false),
}
}
fn infer_method_result_type(&self, mc: &syn::ExprMethodCall) -> Option<VarType> {
let method = mc.method.to_string();
match method.as_str() {
"len" | "capacity" | "count_ones" | "count_zeros" |
"leading_zeros" | "trailing_zeros" => Some(VarType::Integer),
"is_empty" => Some(VarType::Bool),
"concat" | "to_string" => Some(VarType::String),
"get" => {
if let Expr::Path(path) = &*mc.receiver {
if path.path.segments.len() == 1 {
let name = path.path.segments[0].ident.to_string();
if let Some(VarType::String) = self.get_var_type(&name) {
return Some(VarType::Integer); }
}
}
Some(VarType::Integer)
}
_ => None,
}
}
pub(crate) fn compile_assignment(&mut self, target: &Expr, value: &Expr) -> Result<(), CompileError> {
match target {
Expr::Path(path) => {
if path.path.segments.len() != 1 {
return Err(CompileError("Complex paths not supported in assignment".to_string()));
}
let name = path.path.segments[0].ident.to_string();
match self.get_var_location(&name) {
Some(VarLocation::Register(reg)) => {
self.compile_expr(value)?;
self.emit_pop_reg(reg);
}
Some(VarLocation::Array(reg, _)) | Some(VarLocation::String(reg)) => {
self.compile_expr(value)?;
self.emit_pop_reg(reg);
}
Some(VarLocation::InputOffset(_)) => {
return Err(CompileError("Cannot assign to function argument".to_string()));
}
None => {
return Err(CompileError(format!("Unknown variable: {}", name)));
}
}
}
Expr::Index(index) => {
self.compile_index_assignment(&index.expr, &index.index, value)?;
}
_ => {
return Err(CompileError("Only simple variable or index assignment supported".to_string()));
}
}
Ok(())
}
pub(crate) fn compile_assign_op(&mut self, target: &Expr, op: &BinOp, value: &Expr) -> Result<(), CompileError> {
let reg = if let Expr::Path(path) = target {
if path.path.segments.len() == 1 {
let name = path.path.segments[0].ident.to_string();
match self.get_var_location(&name) {
Some(VarLocation::Register(reg)) => reg,
Some(VarLocation::Array(reg, _)) => reg,
Some(VarLocation::String(reg)) => reg,
Some(VarLocation::InputOffset(_)) => {
return Err(CompileError("Cannot assign to function argument".to_string()));
}
None => {
return Err(CompileError(format!("Unknown variable: {}", name)));
}
}
} else {
return Err(CompileError("Complex paths not supported".to_string()));
}
} else {
return Err(CompileError("Only simple variable assignment supported".to_string()));
};
self.emit_push_reg(reg);
self.compile_expr(value)?;
match op {
BinOp::AddAssign(_) => self.emit_add(),
BinOp::SubAssign(_) => self.emit_sub(),
BinOp::MulAssign(_) => self.emit_mul(),
BinOp::DivAssign(_) => self.emit_div(),
BinOp::RemAssign(_) => self.emit_mod(),
BinOp::BitXorAssign(_) => self.emit_xor(),
BinOp::BitAndAssign(_) => self.emit_and(),
BinOp::BitOrAssign(_) => self.emit_or(),
BinOp::ShlAssign(_) => self.emit_shl(),
BinOp::ShrAssign(_) => self.emit_shr(),
_ => return Err(CompileError(format!("Unsupported assignment operator: {:?}", op))),
}
self.emit_pop_reg(reg);
Ok(())
}
}