use crate::lcnf::*;
use std::fmt::Write as FmtWrite;
use super::functions::CSHARP_RUNTIME;
use super::functions::*;
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpSwitchCase {
pub label: std::string::String,
pub stmts: Vec<CSharpStmt>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CSharpStmt {
Expr(CSharpExpr),
Assign {
target: CSharpExpr,
value: CSharpExpr,
},
LocalVar {
name: std::string::String,
ty: Option<CSharpType>,
init: Option<CSharpExpr>,
is_const: bool,
},
If {
cond: CSharpExpr,
then_stmts: Vec<CSharpStmt>,
else_stmts: Vec<CSharpStmt>,
},
Switch {
expr: CSharpExpr,
cases: Vec<CSharpSwitchCase>,
default: Vec<CSharpStmt>,
},
While {
cond: CSharpExpr,
body: Vec<CSharpStmt>,
},
For {
init: Option<Box<CSharpStmt>>,
cond: Option<CSharpExpr>,
step: Option<CSharpExpr>,
body: Vec<CSharpStmt>,
},
ForEach {
var_name: std::string::String,
var_ty: Option<CSharpType>,
collection: CSharpExpr,
body: Vec<CSharpStmt>,
},
Return(Option<CSharpExpr>),
Throw(CSharpExpr),
TryCatch {
try_stmts: Vec<CSharpStmt>,
catches: Vec<CSharpCatchClause>,
finally_stmts: Vec<CSharpStmt>,
},
Using {
resource: CSharpExpr,
var_name: Option<std::string::String>,
body: Vec<CSharpStmt>,
},
Lock {
obj: CSharpExpr,
body: Vec<CSharpStmt>,
},
Break,
Continue,
YieldReturn(CSharpExpr),
YieldBreak,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpInterface {
pub name: std::string::String,
pub methods: Vec<CSharpMethod>,
pub properties: Vec<CSharpProperty>,
pub extends: Vec<std::string::String>,
pub visibility: CSharpVisibility,
pub type_params: Vec<std::string::String>,
}
impl CSharpInterface {
pub fn new(name: &str) -> Self {
CSharpInterface {
name: name.to_string(),
methods: Vec::new(),
properties: Vec::new(),
extends: Vec::new(),
visibility: CSharpVisibility::Public,
type_params: Vec::new(),
}
}
pub fn emit(&self, indent: &str) -> std::string::String {
let inner = format!("{} ", indent);
let mut out = std::string::String::new();
let type_params_str = if self.type_params.is_empty() {
std::string::String::new()
} else {
format!("<{}>", self.type_params.join(", "))
};
let extends_str = if self.extends.is_empty() {
std::string::String::new()
} else {
format!(" : {}", self.extends.join(", "))
};
let _ = writeln!(
out,
"{}{} interface {}{}{}",
indent, self.visibility, self.name, type_params_str, extends_str
);
let _ = writeln!(out, "{}{{", indent);
for prop in &self.properties {
out.push_str(&prop.emit(&inner));
}
for method in &self.methods {
let mut iface_method = method.clone();
iface_method.is_abstract = false;
if method.body.is_empty() && method.expr_body.is_none() {
let params_str = method
.params
.iter()
.map(|(n, t)| format!("{} {}", t, n))
.collect::<Vec<_>>()
.join(", ");
let type_params_str = if method.type_params.is_empty() {
std::string::String::new()
} else {
format!("<{}>", method.type_params.join(", "))
};
let _ = writeln!(
out,
"{}{} {} {}{}({});",
inner,
method.visibility,
method.return_type,
method.name,
type_params_str,
params_str
);
} else {
out.push_str(&iface_method.emit(&inner));
}
}
let _ = writeln!(out, "{}}}", indent);
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpField {
pub name: std::string::String,
pub ty: CSharpType,
pub visibility: CSharpVisibility,
pub is_static: bool,
pub is_readonly: bool,
pub is_const: bool,
pub default_value: Option<CSharpExpr>,
}
impl CSharpField {
pub fn new(name: &str, ty: CSharpType) -> Self {
CSharpField {
name: name.to_string(),
ty,
visibility: CSharpVisibility::Private,
is_static: false,
is_readonly: false,
is_const: false,
default_value: None,
}
}
pub fn emit(&self, indent: &str) -> std::string::String {
let mut out = std::string::String::new();
let mut mods = vec![format!("{}", self.visibility)];
if self.is_static {
mods.push("static".to_string());
}
if self.is_const {
mods.push("const".to_string());
}
if self.is_readonly {
mods.push("readonly".to_string());
}
if let Some(val) = &self.default_value {
let _ = writeln!(
out,
"{}{} {} {} = {};",
indent,
mods.join(" "),
self.ty,
self.name,
val
);
} else {
let _ = writeln!(
out,
"{}{} {} {};",
indent,
mods.join(" "),
self.ty,
self.name
);
}
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpMethod {
pub name: std::string::String,
pub return_type: CSharpType,
pub params: Vec<(std::string::String, CSharpType)>,
pub body: Vec<CSharpStmt>,
pub visibility: CSharpVisibility,
pub is_static: bool,
pub is_async: bool,
pub is_override: bool,
pub is_virtual: bool,
pub is_abstract: bool,
pub type_params: Vec<std::string::String>,
pub expr_body: Option<CSharpExpr>,
}
impl CSharpMethod {
pub fn new(name: &str, return_type: CSharpType) -> Self {
CSharpMethod {
name: name.to_string(),
return_type,
params: Vec::new(),
body: Vec::new(),
visibility: CSharpVisibility::Public,
is_static: false,
is_async: false,
is_override: false,
is_virtual: false,
is_abstract: false,
type_params: Vec::new(),
expr_body: None,
}
}
pub fn emit(&self, indent: &str) -> std::string::String {
let inner = format!("{} ", indent);
let mut out = std::string::String::new();
let mut mods = vec![format!("{}", self.visibility)];
if self.is_static {
mods.push("static".to_string());
}
if self.is_async {
mods.push("async".to_string());
}
if self.is_abstract {
mods.push("abstract".to_string());
}
if self.is_override {
mods.push("override".to_string());
}
if self.is_virtual {
mods.push("virtual".to_string());
}
let type_params_str = if self.type_params.is_empty() {
std::string::String::new()
} else {
format!("<{}>", self.type_params.join(", "))
};
let params_str = self
.params
.iter()
.map(|(n, t)| format!("{} {}", t, n))
.collect::<Vec<_>>()
.join(", ");
let _ = write!(
out,
"{}{} {} {}{}({})",
indent,
mods.join(" "),
self.return_type,
self.name,
type_params_str,
params_str
);
if self.is_abstract {
let _ = writeln!(out, ";");
return out;
}
if let Some(expr) = &self.expr_body {
let _ = writeln!(out, " => {};", expr);
return out;
}
let _ = writeln!(out);
let _ = writeln!(out, "{}{{", indent);
emit_stmts(&self.body, &inner, &mut out);
let _ = writeln!(out, "{}}}", indent);
out
}
}
#[derive(Debug, Clone)]
pub struct CSharpModule {
pub namespace: std::string::String,
pub using_directives: Vec<std::string::String>,
pub classes: Vec<CSharpClass>,
pub records: Vec<CSharpRecord>,
pub interfaces: Vec<CSharpInterface>,
pub enums: Vec<CSharpEnum>,
pub header_comment: Option<std::string::String>,
pub nullable_enable: bool,
}
impl CSharpModule {
pub fn new(namespace: &str) -> Self {
CSharpModule {
namespace: namespace.to_string(),
using_directives: Vec::new(),
classes: Vec::new(),
records: Vec::new(),
interfaces: Vec::new(),
enums: Vec::new(),
header_comment: None,
nullable_enable: true,
}
}
pub fn add_using(&mut self, ns: &str) {
if !self.using_directives.iter().any(|u| u == ns) {
self.using_directives.push(ns.to_string());
}
}
pub fn emit(&self) -> std::string::String {
let mut out = std::string::String::new();
if let Some(comment) = &self.header_comment {
for line in comment.lines() {
let _ = writeln!(out, "// {}", line);
}
let _ = writeln!(out);
}
if self.nullable_enable {
let _ = writeln!(out, "#nullable enable");
let _ = writeln!(out);
}
let mut usings = self.using_directives.clone();
usings.sort();
usings.dedup();
for u in &usings {
let _ = writeln!(out, "using {};", u);
}
if !usings.is_empty() {
let _ = writeln!(out);
}
let _ = writeln!(out, "namespace {};", self.namespace);
let _ = writeln!(out);
for e in &self.enums {
out.push_str(&e.emit(""));
let _ = writeln!(out);
}
for iface in &self.interfaces {
out.push_str(&iface.emit(""));
let _ = writeln!(out);
}
for rec in &self.records {
out.push_str(&rec.emit(""));
let _ = writeln!(out);
}
for cls in &self.classes {
out.push_str(&cls.emit(""));
let _ = writeln!(out);
}
out.push_str(CSHARP_RUNTIME);
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpConstructor {
pub class_name: std::string::String,
pub params: Vec<(std::string::String, CSharpType)>,
pub body: Vec<CSharpStmt>,
pub visibility: CSharpVisibility,
pub base_call: Option<(bool, Vec<CSharpExpr>)>,
}
impl CSharpConstructor {
pub fn new(class_name: &str) -> Self {
CSharpConstructor {
class_name: class_name.to_string(),
params: Vec::new(),
body: Vec::new(),
visibility: CSharpVisibility::Public,
base_call: None,
}
}
pub fn emit(&self, indent: &str) -> std::string::String {
let inner = format!("{} ", indent);
let mut out = std::string::String::new();
let params_str = self
.params
.iter()
.map(|(n, t)| format!("{} {}", t, n))
.collect::<Vec<_>>()
.join(", ");
let base_str = match &self.base_call {
None => std::string::String::new(),
Some((is_base, args)) => {
let kw = if *is_base { "base" } else { "this" };
let args_str = args
.iter()
.map(|a| format!("{}", a))
.collect::<Vec<_>>()
.join(", ");
format!(" : {}({})", kw, args_str)
}
};
let _ = writeln!(
out,
"{}{} {}({}){}",
indent, self.visibility, self.class_name, params_str, base_str
);
let _ = writeln!(out, "{}{{", indent);
emit_stmts(&self.body, &inner, &mut out);
let _ = writeln!(out, "{}}}", indent);
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CSharpInterpolationPart {
Text(std::string::String),
Expr(CSharpExpr),
ExprFmt(CSharpExpr, std::string::String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpClass {
pub name: std::string::String,
pub base_class: Option<std::string::String>,
pub interfaces: Vec<std::string::String>,
pub methods: Vec<CSharpMethod>,
pub properties: Vec<CSharpProperty>,
pub constructors: Vec<CSharpConstructor>,
pub is_sealed: bool,
pub is_abstract: bool,
pub is_static: bool,
pub is_partial: bool,
pub visibility: CSharpVisibility,
pub type_params: Vec<std::string::String>,
pub fields: Vec<CSharpField>,
}
impl CSharpClass {
pub fn new(name: &str) -> Self {
CSharpClass {
name: name.to_string(),
base_class: None,
interfaces: Vec::new(),
methods: Vec::new(),
properties: Vec::new(),
constructors: Vec::new(),
is_sealed: false,
is_abstract: false,
is_static: false,
is_partial: false,
visibility: CSharpVisibility::Public,
type_params: Vec::new(),
fields: Vec::new(),
}
}
pub fn emit(&self, indent: &str) -> std::string::String {
let inner = format!("{} ", indent);
let mut out = std::string::String::new();
let mut mods = vec![format!("{}", self.visibility)];
if self.is_static {
mods.push("static".to_string());
}
if self.is_sealed {
mods.push("sealed".to_string());
}
if self.is_abstract {
mods.push("abstract".to_string());
}
if self.is_partial {
mods.push("partial".to_string());
}
let type_params_str = if self.type_params.is_empty() {
std::string::String::new()
} else {
format!("<{}>", self.type_params.join(", "))
};
let mut inherits: Vec<std::string::String> = Vec::new();
if let Some(base) = &self.base_class {
inherits.push(base.clone());
}
inherits.extend(self.interfaces.iter().cloned());
let inherit_str = if inherits.is_empty() {
std::string::String::new()
} else {
format!(" : {}", inherits.join(", "))
};
let _ = writeln!(
out,
"{}{} class {}{}{}",
indent,
mods.join(" "),
self.name,
type_params_str,
inherit_str
);
let _ = writeln!(out, "{}{{", indent);
for field in &self.fields {
out.push_str(&field.emit(&inner));
}
for ctor in &self.constructors {
out.push_str(&ctor.emit(&inner));
}
for prop in &self.properties {
out.push_str(&prop.emit(&inner));
}
for method in &self.methods {
out.push_str(&method.emit(&inner));
}
let _ = writeln!(out, "{}}}", indent);
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CSharpExpr {
Lit(CSharpLit),
Var(std::string::String),
BinOp {
op: std::string::String,
lhs: Box<CSharpExpr>,
rhs: Box<CSharpExpr>,
},
UnaryOp {
op: std::string::String,
operand: Box<CSharpExpr>,
},
Call {
callee: Box<CSharpExpr>,
args: Vec<CSharpExpr>,
},
MethodCall {
receiver: Box<CSharpExpr>,
method: std::string::String,
type_args: Vec<CSharpType>,
args: Vec<CSharpExpr>,
},
New {
ty: CSharpType,
args: Vec<CSharpExpr>,
},
Lambda {
params: Vec<(std::string::String, Option<CSharpType>)>,
body: Box<CSharpExpr>,
},
Ternary {
cond: Box<CSharpExpr>,
then_expr: Box<CSharpExpr>,
else_expr: Box<CSharpExpr>,
},
Null,
Default(Option<CSharpType>),
NameOf(std::string::String),
TypeOf(CSharpType),
Await(Box<CSharpExpr>),
Throw(Box<CSharpExpr>),
Is {
expr: Box<CSharpExpr>,
pattern: std::string::String,
},
As {
expr: Box<CSharpExpr>,
ty: CSharpType,
},
Member(Box<CSharpExpr>, std::string::String),
Index(Box<CSharpExpr>, Box<CSharpExpr>),
SwitchExpr {
scrutinee: Box<CSharpExpr>,
arms: Vec<CSharpSwitchArm>,
},
Interpolated(Vec<CSharpInterpolationPart>),
CollectionExpr(Vec<CSharpExpr>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpProperty {
pub name: std::string::String,
pub ty: CSharpType,
pub visibility: CSharpVisibility,
pub has_getter: bool,
pub has_setter: bool,
pub is_init_only: bool,
pub is_static: bool,
pub default_value: Option<CSharpExpr>,
pub expr_body: Option<CSharpExpr>,
}
impl CSharpProperty {
pub fn new_auto(name: &str, ty: CSharpType) -> Self {
CSharpProperty {
name: name.to_string(),
ty,
visibility: CSharpVisibility::Public,
has_getter: true,
has_setter: true,
is_init_only: false,
is_static: false,
default_value: None,
expr_body: None,
}
}
pub fn emit(&self, indent: &str) -> std::string::String {
let mut out = std::string::String::new();
let mut mods = vec![format!("{}", self.visibility)];
if self.is_static {
mods.push("static".to_string());
}
if let Some(expr) = &self.expr_body {
let _ = writeln!(
out,
"{}{} {} {} => {};",
indent,
mods.join(" "),
self.ty,
self.name,
expr
);
return out;
}
let accessors = match (self.has_getter, self.has_setter, self.is_init_only) {
(true, true, false) => "{ get; set; }",
(true, false, false) => "{ get; }",
(true, _, true) => "{ get; init; }",
(false, true, false) => "{ set; }",
_ => "{ get; set; }",
};
if let Some(val) = &self.default_value {
let _ = writeln!(
out,
"{}{} {} {} {} = {};",
indent,
mods.join(" "),
self.ty,
self.name,
accessors,
val
);
} else {
let _ = writeln!(
out,
"{}{} {} {} {};",
indent,
mods.join(" "),
self.ty,
self.name,
accessors
);
}
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpEnum {
pub name: std::string::String,
pub variants: Vec<(std::string::String, Option<i64>)>,
pub visibility: CSharpVisibility,
pub underlying_type: Option<CSharpType>,
}
impl CSharpEnum {
pub fn new(name: &str) -> Self {
CSharpEnum {
name: name.to_string(),
variants: Vec::new(),
visibility: CSharpVisibility::Public,
underlying_type: None,
}
}
pub fn emit(&self, indent: &str) -> std::string::String {
let inner = format!("{} ", indent);
let mut out = std::string::String::new();
let base_str = match &self.underlying_type {
None => std::string::String::new(),
Some(t) => format!(" : {}", t),
};
let _ = writeln!(
out,
"{}{} enum {}{}",
indent, self.visibility, self.name, base_str
);
let _ = writeln!(out, "{}{{", indent);
for (name, val) in &self.variants {
if let Some(v) = val {
let _ = writeln!(out, "{}{} = {},", inner, name, v);
} else {
let _ = writeln!(out, "{}{},", inner, name);
}
}
let _ = writeln!(out, "{}}}", indent);
out
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CSharpVisibility {
Public,
Private,
Protected,
Internal,
ProtectedInternal,
PrivateProtected,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CSharpType {
Int,
Long,
Double,
Float,
Bool,
String,
Void,
Object,
List(Box<CSharpType>),
Dict(Box<CSharpType>, Box<CSharpType>),
Tuple(Vec<CSharpType>),
Custom(std::string::String),
Nullable(Box<CSharpType>),
Task(Box<CSharpType>),
IEnumerable(Box<CSharpType>),
Func(Vec<CSharpType>, Box<CSharpType>),
Action(Vec<CSharpType>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum CSharpLit {
Int(i64),
Long(i64),
Bool(bool),
Str(std::string::String),
Null,
Float(f64),
Double(f64),
Char(char),
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpCatchClause {
pub exception_type: CSharpType,
pub var_name: std::string::String,
pub stmts: Vec<CSharpStmt>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpRecord {
pub name: std::string::String,
pub fields: Vec<(std::string::String, CSharpType)>,
pub methods: Vec<CSharpMethod>,
pub is_readonly: bool,
pub is_sealed: bool,
pub base_record: Option<std::string::String>,
pub interfaces: Vec<std::string::String>,
pub visibility: CSharpVisibility,
}
impl CSharpRecord {
pub fn new(name: &str) -> Self {
CSharpRecord {
name: name.to_string(),
fields: Vec::new(),
methods: Vec::new(),
is_readonly: false,
is_sealed: false,
base_record: None,
interfaces: Vec::new(),
visibility: CSharpVisibility::Public,
}
}
pub fn emit(&self, indent: &str) -> std::string::String {
let inner = format!("{} ", indent);
let mut out = std::string::String::new();
let mut mods = vec![format!("{}", self.visibility)];
if self.is_sealed {
mods.push("sealed".to_string());
}
if self.is_readonly {
mods.push("readonly".to_string());
}
let fields_str = self
.fields
.iter()
.map(|(n, t)| format!("{} {}", t, n))
.collect::<Vec<_>>()
.join(", ");
let mut inherits: Vec<std::string::String> = Vec::new();
if let Some(base) = &self.base_record {
inherits.push(base.clone());
}
inherits.extend(self.interfaces.iter().cloned());
let inherit_str = if inherits.is_empty() {
std::string::String::new()
} else {
format!(" : {}", inherits.join(", "))
};
let record_kw = if self.is_readonly {
"record struct"
} else {
"record"
};
if self.methods.is_empty() {
let _ = writeln!(
out,
"{}{} {} {}({}){}",
indent,
mods.join(" "),
record_kw,
self.name,
fields_str,
inherit_str
);
if out.ends_with('\n') {
out.pop();
out.push(';');
out.push('\n');
}
} else {
let _ = writeln!(
out,
"{}{} {} {}({}){}",
indent,
mods.join(" "),
record_kw,
self.name,
fields_str,
inherit_str
);
let _ = writeln!(out, "{}{{", indent);
for method in &self.methods {
out.push_str(&method.emit(&inner));
}
let _ = writeln!(out, "{}}}", indent);
}
out
}
}
pub struct CSharpBackend {
pub emit_public: bool,
pub emit_comments: bool,
pub(super) var_counter: u64,
pub prefer_async: bool,
}
impl CSharpBackend {
pub fn new() -> Self {
CSharpBackend {
emit_public: true,
emit_comments: true,
var_counter: 0,
prefer_async: false,
}
}
pub fn fresh_var(&mut self) -> std::string::String {
let n = self.var_counter;
self.var_counter += 1;
format!("_cs{}", n)
}
pub fn mangle_name(name: &str) -> std::string::String {
if name.is_empty() {
return "ox_empty".to_string();
}
let mangled: std::string::String = name
.chars()
.map(|c| {
if c.is_alphanumeric() || c == '_' {
c
} else {
'_'
}
})
.collect();
if mangled
.chars()
.next()
.map(|c| c.is_ascii_digit())
.unwrap_or(false)
|| is_csharp_keyword(&mangled)
{
format!("ox_{}", mangled)
} else {
mangled
}
}
pub fn compile_decl(&self, decl: &LcnfFunDecl) -> CSharpMethod {
let ret_ty = lcnf_type_to_csharp(&decl.ret_type);
let mut method = CSharpMethod::new(&Self::mangle_name(&decl.name), ret_ty);
method.visibility = if self.emit_public {
CSharpVisibility::Public
} else {
CSharpVisibility::Private
};
method.is_static = true;
for param in &decl.params {
if param.erased {
continue;
}
let param_ty = lcnf_type_to_csharp(¶m.ty);
let param_name = format!("_x{}", param.id.0);
method.params.push((param_name, param_ty));
}
let mut stmts: Vec<CSharpStmt> = Vec::new();
let result = self.compile_expr_to_stmts(&decl.body, &mut stmts);
stmts.push(CSharpStmt::Return(Some(result)));
method.body = stmts;
method
}
pub(super) fn compile_expr_to_stmts(
&self,
expr: &LcnfExpr,
stmts: &mut Vec<CSharpStmt>,
) -> CSharpExpr {
match expr {
LcnfExpr::Return(arg) => self.compile_arg(arg),
LcnfExpr::Unreachable => CSharpExpr::Throw(Box::new(CSharpExpr::New {
ty: CSharpType::Custom("InvalidOperationException".to_string()),
args: vec![CSharpExpr::Lit(CSharpLit::Str(
"OxiLean: unreachable code reached".to_string(),
))],
})),
LcnfExpr::TailCall(func, args) => {
let callee = self.compile_arg(func);
let cs_args: Vec<CSharpExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
CSharpExpr::Call {
callee: Box::new(callee),
args: cs_args,
}
}
LcnfExpr::Let {
id, value, body, ..
} => {
let val_expr = self.compile_let_value(value);
let var_name = format!("_x{}", id.0);
stmts.push(CSharpStmt::LocalVar {
name: var_name,
ty: None,
init: Some(val_expr),
is_const: false,
});
self.compile_expr_to_stmts(body, stmts)
}
LcnfExpr::Case {
scrutinee,
alts,
default,
..
} => {
let scrutinee_expr = CSharpExpr::Var(format!("_x{}", scrutinee.0));
let tag_expr =
CSharpExpr::Member(Box::new(scrutinee_expr.clone()), "Tag".to_string());
let result_var = format!("_cs_case{}", stmts.len());
stmts.push(CSharpStmt::LocalVar {
name: result_var.clone(),
ty: Some(CSharpType::Object),
init: Some(CSharpExpr::Null),
is_const: false,
});
let mut cases: Vec<CSharpSwitchCase> = Vec::new();
for alt in alts {
let mut branch_stmts: Vec<CSharpStmt> = Vec::new();
for (field_idx, param) in alt.params.iter().enumerate() {
if param.erased {
continue;
}
let field_access = CSharpExpr::Member(
Box::new(scrutinee_expr.clone()),
format!("Field{}", field_idx),
);
branch_stmts.push(CSharpStmt::LocalVar {
name: format!("_x{}", param.id.0),
ty: Some(lcnf_type_to_csharp(¶m.ty)),
init: Some(field_access),
is_const: false,
});
}
let branch_result = self.compile_expr_to_stmts(&alt.body, &mut branch_stmts);
branch_stmts.push(CSharpStmt::Assign {
target: CSharpExpr::Var(result_var.clone()),
value: branch_result,
});
branch_stmts.push(CSharpStmt::Break);
cases.push(CSharpSwitchCase {
label: format!("{}", alt.ctor_tag),
stmts: branch_stmts,
});
}
let mut default_stmts: Vec<CSharpStmt> = Vec::new();
if let Some(def) = default {
let def_result = self.compile_expr_to_stmts(def, &mut default_stmts);
default_stmts.push(CSharpStmt::Assign {
target: CSharpExpr::Var(result_var.clone()),
value: def_result,
});
} else {
default_stmts.push(CSharpStmt::Throw(CSharpExpr::New {
ty: CSharpType::Custom("InvalidOperationException".to_string()),
args: vec![CSharpExpr::Lit(CSharpLit::Str(
"OxiLean: unreachable case".to_string(),
))],
}));
}
stmts.push(CSharpStmt::Switch {
expr: tag_expr,
cases,
default: default_stmts,
});
CSharpExpr::Var(result_var)
}
}
}
pub(super) fn compile_let_value(&self, value: &LcnfLetValue) -> CSharpExpr {
match value {
LcnfLetValue::Lit(lit) => self.compile_lit(lit),
LcnfLetValue::Erased => CSharpExpr::Null,
LcnfLetValue::FVar(id) => CSharpExpr::Var(format!("_x{}", id.0)),
LcnfLetValue::App(func, args) => {
let callee = self.compile_arg(func);
let cs_args: Vec<CSharpExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
CSharpExpr::Call {
callee: Box::new(callee),
args: cs_args,
}
}
LcnfLetValue::Proj(_name, idx, var) => {
let base = CSharpExpr::Var(format!("_x{}", var.0));
CSharpExpr::Member(Box::new(base), format!("Field{}", idx))
}
LcnfLetValue::Ctor(name, _tag, args) => {
let ctor_name = Self::mangle_name(name);
let cs_args: Vec<CSharpExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
CSharpExpr::New {
ty: CSharpType::Custom(ctor_name),
args: cs_args,
}
}
LcnfLetValue::Reset(_var) => CSharpExpr::Null,
LcnfLetValue::Reuse(_slot, name, _tag, args) => {
let ctor_name = Self::mangle_name(name);
let cs_args: Vec<CSharpExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
CSharpExpr::New {
ty: CSharpType::Custom(ctor_name),
args: cs_args,
}
}
}
}
pub(super) fn compile_arg(&self, arg: &LcnfArg) -> CSharpExpr {
match arg {
LcnfArg::Var(id) => CSharpExpr::Var(format!("_x{}", id.0)),
LcnfArg::Lit(lit) => self.compile_lit(lit),
LcnfArg::Erased => CSharpExpr::Null,
LcnfArg::Type(_) => CSharpExpr::Null,
}
}
pub(super) fn compile_lit(&self, lit: &LcnfLit) -> CSharpExpr {
match lit {
LcnfLit::Nat(n) => CSharpExpr::Lit(CSharpLit::Long(*n as i64)),
LcnfLit::Str(s) => CSharpExpr::Lit(CSharpLit::Str(s.clone())),
}
}
pub fn emit_module(&self, namespace: &str, decls: &[LcnfFunDecl]) -> CSharpModule {
let mut module = CSharpModule::new(namespace);
module.header_comment = Some(format!(
"OxiLean-generated C# module: {}\nGenerated by OxiLean CSharpBackend",
namespace
));
module.add_using("System");
module.add_using("System.Collections.Generic");
module.add_using("System.Linq");
module.add_using("System.Threading.Tasks");
let mut runtime_class = CSharpClass::new("OxiLeanRuntime");
runtime_class.is_static = true;
runtime_class.visibility = CSharpVisibility::Internal;
for decl in decls {
let method = self.compile_decl(decl);
runtime_class.methods.push(method);
}
module.classes.push(runtime_class);
module
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CSharpSwitchArm {
pub pattern: std::string::String,
pub guard: Option<CSharpExpr>,
pub body: CSharpExpr,
}