use super::super::Transpiler;
use crate::frontend::ast::{Expr, ExprKind, UnaryOp};
use anyhow::Result;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
impl Transpiler {
pub fn transpile_unary(&self, op: UnaryOp, operand: &Expr) -> Result<TokenStream> {
let operand_tokens = self.transpile_expr(operand)?;
Ok(match op {
UnaryOp::Not | UnaryOp::BitwiseNot => quote! { !#operand_tokens },
UnaryOp::Negate => quote! { -#operand_tokens },
UnaryOp::Reference => quote! { &#operand_tokens },
UnaryOp::MutableReference => quote! { &mut #operand_tokens }, UnaryOp::Deref => quote! { *#operand_tokens },
})
}
pub fn transpile_await(&self, expr: &Expr) -> Result<TokenStream> {
let expr_tokens = self.transpile_expr(expr)?;
Ok(quote! { #expr_tokens.await })
}
pub fn transpile_spawn(&self, actor: &Expr) -> Result<TokenStream> {
if let ExprKind::StructLiteral { name, fields, .. } = &actor.kind {
let actor_name = format_ident!("{}", name);
let field_tokens = fields
.iter()
.map(|(name, value)| {
let field_name = format_ident!("{}", name);
let value_tokens = self.transpile_expr(value)?;
Ok(quote! { #field_name: #value_tokens })
})
.collect::<Result<Vec<_>>>()?;
Ok(quote! {
std::sync::Arc::new(std::sync::Mutex::new(#actor_name {
#(#field_tokens),*
}))
})
} else {
let actor_tokens = self.transpile_expr(actor)?;
Ok(quote! { #actor_tokens })
}
}
pub fn transpile_async_block(&self, body: &Expr) -> Result<TokenStream> {
let body_tokens = self.transpile_expr(body)?;
Ok(quote! { { #body_tokens } })
}
pub fn transpile_async_lambda(&self, params: &[String], body: &Expr) -> Result<TokenStream> {
let param_idents: Vec<proc_macro2::Ident> =
params.iter().map(|p| format_ident!("{}", p)).collect();
let body_tokens = self.transpile_expr(body)?;
Ok(quote! { |#(#param_idents),*| async move { #body_tokens } })
}
pub fn transpile_throw(&self, expr: &Expr) -> Result<TokenStream> {
let expr_tokens = self.transpile_expr(expr)?;
Ok(quote! {
panic!(#expr_tokens)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::{Expr, ExprKind, Literal, Span};
fn test_transpiler() -> Transpiler {
Transpiler::new()
}
fn ident_expr(name: &str) -> Expr {
Expr {
kind: ExprKind::Identifier(name.to_string()),
span: Span::default(),
attributes: vec![],
leading_comments: vec![],
trailing_comment: None,
}
}
fn string_expr(value: &str) -> Expr {
Expr {
kind: ExprKind::Literal(Literal::String(value.to_string())),
span: Span::default(),
attributes: vec![],
leading_comments: vec![],
trailing_comment: None,
}
}
#[test]
fn test_transpile_unary_not() {
let transpiler = test_transpiler();
let operand = ident_expr("x");
let result = transpiler
.transpile_unary(UnaryOp::Not, &operand)
.expect("operation should succeed in test");
assert_eq!(result.to_string(), "! x");
}
#[test]
fn test_transpile_unary_bitwise_not() {
let transpiler = test_transpiler();
let operand = ident_expr("bits");
let result = transpiler
.transpile_unary(UnaryOp::BitwiseNot, &operand)
.expect("operation should succeed in test");
assert_eq!(result.to_string(), "! bits");
}
#[test]
fn test_transpile_unary_negate() {
let transpiler = test_transpiler();
let operand = ident_expr("num");
let result = transpiler
.transpile_unary(UnaryOp::Negate, &operand)
.expect("operation should succeed in test");
assert_eq!(result.to_string(), "- num");
}
#[test]
fn test_transpile_unary_reference() {
let transpiler = test_transpiler();
let operand = ident_expr("value");
let result = transpiler
.transpile_unary(UnaryOp::Reference, &operand)
.expect("operation should succeed in test");
assert_eq!(result.to_string(), "& value");
}
#[test]
fn test_transpile_unary_mutable_reference() {
let transpiler = test_transpiler();
let operand = ident_expr("data");
let result = transpiler
.transpile_unary(UnaryOp::MutableReference, &operand)
.expect("operation should succeed in test");
assert_eq!(result.to_string(), "& mut data");
}
#[test]
fn test_transpile_unary_deref() {
let transpiler = test_transpiler();
let operand = ident_expr("ptr");
let result = transpiler
.transpile_unary(UnaryOp::Deref, &operand)
.expect("operation should succeed in test");
assert_eq!(result.to_string(), "* ptr");
}
#[test]
fn test_transpile_await_basic() {
let transpiler = test_transpiler();
let expr = ident_expr("future");
let result = transpiler
.transpile_await(&expr)
.expect("operation should succeed in test");
assert_eq!(result.to_string(), "future . await");
}
#[test]
fn test_transpile_spawn_struct_literal() {
let transpiler = test_transpiler();
let actor = Expr {
kind: ExprKind::StructLiteral {
name: "Worker".to_string(),
fields: vec![("id".to_string(), ident_expr("worker_id"))],
base: None,
},
span: Span::default(),
attributes: vec![],
leading_comments: vec![],
trailing_comment: None,
};
let result = transpiler
.transpile_spawn(&actor)
.expect("operation should succeed in test");
let result_str = result.to_string();
assert!(result_str.contains("Arc"));
assert!(result_str.contains("Mutex"));
assert!(result_str.contains("Worker"));
assert!(result_str.contains("id"));
}
#[test]
fn test_transpile_spawn_non_struct() {
let transpiler = test_transpiler();
let actor = ident_expr("actor_instance");
let result = transpiler
.transpile_spawn(&actor)
.expect("operation should succeed in test");
assert_eq!(result.to_string(), "actor_instance");
}
#[test]
fn test_transpile_async_block_basic() {
let transpiler = test_transpiler();
let body = ident_expr("value");
let result = transpiler
.transpile_async_block(&body)
.expect("operation should succeed in test");
assert_eq!(result.to_string(), "{ value }");
}
#[test]
fn test_transpile_async_lambda_no_params() {
let transpiler = test_transpiler();
let params = vec![];
let body = string_expr("result");
let result = transpiler
.transpile_async_lambda(¶ms, &body)
.expect("operation should succeed in test");
let result_str = result.to_string();
assert!(result_str.contains("async move"));
assert!(result_str.contains("\"result\""));
}
#[test]
fn test_transpile_async_lambda_single_param() {
let transpiler = test_transpiler();
let params = vec!["x".to_string()];
let body = ident_expr("x");
let result = transpiler
.transpile_async_lambda(¶ms, &body)
.expect("operation should succeed in test");
let result_str = result.to_string();
assert!(result_str.contains("| x |"));
assert!(result_str.contains("async move"));
}
#[test]
fn test_transpile_async_lambda_multiple_params() {
let transpiler = test_transpiler();
let params = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let body = ident_expr("result");
let result = transpiler
.transpile_async_lambda(¶ms, &body)
.expect("operation should succeed in test");
let result_str = result.to_string();
assert!(result_str.contains('a'));
assert!(result_str.contains('b'));
assert!(result_str.contains('c'));
assert!(result_str.contains("async move"));
}
#[test]
fn test_transpile_throw_string() {
let transpiler = test_transpiler();
let expr = string_expr("Error occurred");
let result = transpiler
.transpile_throw(&expr)
.expect("operation should succeed in test");
let result_str = result.to_string();
assert!(result_str.contains("panic"));
assert!(result_str.contains("\"Error occurred\""));
}
#[test]
fn test_transpile_throw_identifier() {
let transpiler = test_transpiler();
let expr = ident_expr("error_msg");
let result = transpiler
.transpile_throw(&expr)
.expect("operation should succeed in test");
let result_str = result.to_string();
assert!(result_str.contains("panic"));
assert!(result_str.contains("error_msg"));
}
}