mod decl;
mod expr;
mod stmt;
use crate::lower::{CBinOp, CDecl, CExpr, CFile, CFnDef, CStmt, CType, CUnaryOp};
pub struct Emitter {
output: String,
indent: usize,
}
impl Emitter {
pub fn new() -> Self {
Self {
output: String::new(),
indent: 0,
}
}
pub fn emit_header(&mut self, file: &CFile, module_name: &str) -> String {
self.output.clear();
let guard = format!("{}_H", module_name.to_uppercase().replace('-', "_"));
self.line(&format!("#ifndef {}", guard));
self.line(&format!("#define {}", guard));
self.blank();
self.line("#include <stdint.h>");
self.line("#include <stddef.h>");
self.line("#include <stdbool.h>");
self.blank();
for def in &file.type_defs {
self.emit_decl(def);
self.blank();
}
let mut fn_defs: Vec<_> = file.fn_defs.iter().collect();
fn_defs.sort_by_key(|f| &f.name);
for def in &fn_defs {
self.emit_fn_forward_decl(def);
}
self.blank();
self.line(&format!("#endif /* {} */", guard));
self.output.clone()
}
pub fn emit(&mut self, file: &CFile) -> String {
self.output.clear();
self.line("/* Generated by fastc - do not edit */");
self.blank();
let mut includes = file.includes.clone();
includes.sort();
for inc in &includes {
self.line(&format!("#include {}", inc));
}
self.blank();
for decl in &file.forward_decls {
self.emit_decl(decl);
}
for def in &file.type_defs {
self.emit_decl(def);
self.blank();
}
for proto in &file.fn_protos {
self.emit_fn_proto(proto);
}
if !file.fn_protos.is_empty() {
self.blank();
}
let mut fn_defs: Vec<_> = file.fn_defs.iter().collect();
fn_defs.sort_by_key(|f| &f.name);
for def in &fn_defs {
self.emit_fn_forward_decl(def);
}
if !fn_defs.is_empty() {
self.blank();
}
for (i, def) in fn_defs.iter().enumerate() {
self.emit_fn_def(def);
if i < fn_defs.len() - 1 {
self.blank();
}
}
self.output.clone()
}
fn emit_decl(&mut self, decl: &CDecl) {
match decl {
CDecl::Struct { name, fields } => {
self.line(&format!("typedef struct {} {{", name));
self.indent += 1;
for field in fields {
let base = self.type_base_string(&field.ty);
let suffix = self.type_suffix_string(&field.ty);
self.line(&format!("{} {}{};", base, field.name, suffix));
}
self.indent -= 1;
self.line(&format!("}} {};", name));
}
CDecl::Typedef { name, ty } => {
self.line(&format!("typedef {} {};", self.type_to_string(ty), name));
}
CDecl::Enum { name, variants } => {
self.line(&format!("typedef enum {} {{", name));
self.indent += 1;
for (i, var) in variants.iter().enumerate() {
if i < variants.len() - 1 {
self.line(&format!("{},", var));
} else {
self.line(var);
}
}
self.indent -= 1;
self.line(&format!("}} {};", name));
}
}
}
fn emit_fn_proto(&mut self, proto: &crate::lower::CFnProto) {
let params = if proto.params.is_empty() {
"void".to_string()
} else {
proto
.params
.iter()
.map(|p| {
let base = self.type_base_string(&p.ty);
let suffix = self.type_suffix_string(&p.ty);
format!("{} {}{}", base, p.name, suffix)
})
.collect::<Vec<_>>()
.join(", ")
};
self.line(&format!(
"{} {}({});",
self.type_to_string(&proto.return_type),
proto.name,
params
));
}
fn emit_fn_forward_decl(&mut self, def: &CFnDef) {
let params = if def.params.is_empty() {
"void".to_string()
} else {
def.params
.iter()
.map(|p| {
let base = self.type_base_string(&p.ty);
let suffix = self.type_suffix_string(&p.ty);
format!("{} {}{}", base, p.name, suffix)
})
.collect::<Vec<_>>()
.join(", ")
};
self.line(&format!(
"{} {}({});",
self.type_to_string(&def.return_type),
def.name,
params
));
}
fn emit_fn_def(&mut self, def: &CFnDef) {
let params = if def.params.is_empty() {
"void".to_string()
} else {
def.params
.iter()
.map(|p| {
let base = self.type_base_string(&p.ty);
let suffix = self.type_suffix_string(&p.ty);
format!("{} {}{}", base, p.name, suffix)
})
.collect::<Vec<_>>()
.join(", ")
};
self.line(&format!(
"{} {}({}) {{",
self.type_to_string(&def.return_type),
def.name,
params
));
self.indent += 1;
for stmt in &def.body {
self.emit_stmt(stmt);
}
self.indent -= 1;
self.line("}");
}
fn emit_stmt(&mut self, stmt: &CStmt) {
match stmt {
CStmt::VarDecl { name, ty, init } => {
let base = self.type_base_string(ty);
let suffix = self.type_suffix_string(ty);
if let Some(init) = init {
self.line(&format!(
"{} {}{} = {};",
base,
name,
suffix,
self.expr_to_string(init)
));
} else {
self.line(&format!("{} {}{};", base, name, suffix));
}
}
CStmt::Assign { lhs, rhs } => {
self.line(&format!(
"{} = {};",
self.expr_to_string(lhs),
self.expr_to_string(rhs)
));
}
CStmt::If { cond, then, else_ } => {
self.line(&format!("if ({}) {{", self.expr_to_string(cond)));
self.indent += 1;
for s in then {
self.emit_stmt(s);
}
self.indent -= 1;
if let Some(else_stmts) = else_ {
self.line("} else {");
self.indent += 1;
for s in else_stmts {
self.emit_stmt(s);
}
self.indent -= 1;
}
self.line("}");
}
CStmt::While { cond, body } => {
self.line(&format!("while ({}) {{", self.expr_to_string(cond)));
self.indent += 1;
for s in body {
self.emit_stmt(s);
}
self.indent -= 1;
self.line("}");
}
CStmt::For {
init,
cond,
step,
body,
} => {
let init_str = init
.as_ref()
.map(|s| self.stmt_to_inline_string(s))
.unwrap_or_default();
let cond_str = cond
.as_ref()
.map(|e| self.expr_to_string(e))
.unwrap_or_default();
let step_str = step
.as_ref()
.map(|e| self.expr_to_string(e))
.unwrap_or_default();
self.line(&format!("for ({}; {}; {}) {{", init_str, cond_str, step_str));
self.indent += 1;
for s in body {
self.emit_stmt(s);
}
self.indent -= 1;
self.line("}");
}
CStmt::Return(value) => {
if let Some(v) = value {
self.line(&format!("return {};", self.expr_to_string(v)));
} else {
self.line("return;");
}
}
CStmt::Expr(expr) => {
self.line(&format!("{};", self.expr_to_string(expr)));
}
CStmt::Block(stmts) => {
self.line("{");
self.indent += 1;
for s in stmts {
self.emit_stmt(s);
}
self.indent -= 1;
self.line("}");
}
CStmt::Goto(label) => {
self.line(&format!("goto {};", label));
}
CStmt::Label(label) => {
let old_indent = self.indent;
self.indent = 0;
self.line(&format!("{}:", label));
self.indent = old_indent;
}
CStmt::Switch {
expr,
cases,
default,
} => {
self.line(&format!("switch ({}) {{", self.expr_to_string(expr)));
self.indent += 1;
for (value, stmts) in cases {
self.line(&format!("case {}:", self.expr_to_string(value)));
self.indent += 1;
for stmt in stmts {
self.emit_stmt(stmt);
}
self.indent -= 1;
}
if let Some(stmts) = default {
self.line("default:");
self.indent += 1;
for stmt in stmts {
self.emit_stmt(stmt);
}
self.indent -= 1;
}
self.indent -= 1;
self.line("}");
}
CStmt::Break => {
self.line("break;");
}
}
}
fn stmt_to_inline_string(&self, stmt: &CStmt) -> String {
match stmt {
CStmt::VarDecl { name, ty, init } => {
let base = self.type_base_string(ty);
let suffix = self.type_suffix_string(ty);
if let Some(init) = init {
format!(
"{} {}{} = {}",
base,
name,
suffix,
self.expr_to_string(init)
)
} else {
format!("{} {}{}", base, name, suffix)
}
}
CStmt::Assign { lhs, rhs } => {
format!("{} = {}", self.expr_to_string(lhs), self.expr_to_string(rhs))
}
CStmt::Expr(expr) => self.expr_to_string(expr),
_ => String::new(),
}
}
fn expr_to_string(&self, expr: &CExpr) -> String {
match expr {
CExpr::IntLit(s) => s.clone(),
CExpr::FloatLit(s) => s.clone(),
CExpr::BoolLit(b) => if *b { "true" } else { "false" }.to_string(),
CExpr::StringLit(s) => format!("\"{}\"", s.escape_default()),
CExpr::Ident(name) => name.clone(),
CExpr::Binary { op, lhs, rhs } => {
format!(
"({} {} {})",
self.expr_to_string(lhs),
self.binop_to_string(*op),
self.expr_to_string(rhs)
)
}
CExpr::Unary { op, operand } => {
format!("({}{})", self.unaryop_to_string(*op), self.expr_to_string(operand))
}
CExpr::Call { func, args } => {
let args_str = args
.iter()
.map(|a| self.expr_to_string(a))
.collect::<Vec<_>>()
.join(", ");
format!("{}({})", self.expr_to_string(func), args_str)
}
CExpr::Field { base, field } => {
format!("{}.{}", self.expr_to_string(base), field)
}
CExpr::Deref(ptr) => {
format!("(*{})", self.expr_to_string(ptr))
}
CExpr::AddrOf(val) => {
format!("(&{})", self.expr_to_string(val))
}
CExpr::Index { base, index } => {
format!("{}[{}]", self.expr_to_string(base), self.expr_to_string(index))
}
CExpr::Cast { ty, expr } => {
format!("(({}){})", self.type_to_string(ty), self.expr_to_string(expr))
}
CExpr::Paren(inner) => {
format!("({})", self.expr_to_string(inner))
}
CExpr::Compound { ty, fields } => {
let fields_str = fields
.iter()
.map(|(name, val)| format!(".{} = {}", name, self.expr_to_string(val)))
.collect::<Vec<_>>()
.join(", ");
format!("(({}){{ {} }})", self.type_to_string(ty), fields_str)
}
}
}
fn type_to_string(&self, ty: &CType) -> String {
match ty {
CType::Void => "void".to_string(),
CType::Bool => "bool".to_string(),
CType::Int8 => "int8_t".to_string(),
CType::Int16 => "int16_t".to_string(),
CType::Int32 => "int32_t".to_string(),
CType::Int64 => "int64_t".to_string(),
CType::UInt8 => "uint8_t".to_string(),
CType::UInt16 => "uint16_t".to_string(),
CType::UInt32 => "uint32_t".to_string(),
CType::UInt64 => "uint64_t".to_string(),
CType::Float => "float".to_string(),
CType::Double => "double".to_string(),
CType::SizeT => "size_t".to_string(),
CType::PtrDiffT => "ptrdiff_t".to_string(),
CType::Ptr(inner) => format!("{}*", self.type_to_string(inner)),
CType::ConstPtr(inner) => format!("const {}*", self.type_to_string(inner)),
CType::Array(inner, size) => format!("{}[{}]", self.type_to_string(inner), size),
CType::Slice(inner) => {
format!("fc_slice_{}", self.type_to_c_name(inner))
}
CType::Opt(inner) => {
format!("fc_opt_{}", self.type_to_c_name(inner))
}
CType::Res(ok_ty, err_ty) => {
format!(
"fc_res_{}_{}",
self.type_to_c_name(ok_ty),
self.type_to_c_name(err_ty)
)
}
CType::Named(name) => name.clone(),
}
}
fn type_to_c_name(&self, ty: &CType) -> String {
match ty {
CType::Int8 => "int8_t".to_string(),
CType::Int16 => "int16_t".to_string(),
CType::Int32 => "int32_t".to_string(),
CType::Int64 => "int64_t".to_string(),
CType::UInt8 => "uint8_t".to_string(),
CType::UInt16 => "uint16_t".to_string(),
CType::UInt32 => "uint32_t".to_string(),
CType::UInt64 => "uint64_t".to_string(),
CType::Float => "float".to_string(),
CType::Double => "double".to_string(),
CType::SizeT => "size_t".to_string(),
CType::PtrDiffT => "ptrdiff_t".to_string(),
CType::Bool => "bool".to_string(),
CType::Named(n) => n.clone(),
CType::Ptr(inner) => format!("ptr_{}", self.type_to_c_name(inner)),
CType::ConstPtr(inner) => format!("cptr_{}", self.type_to_c_name(inner)),
CType::Slice(inner) => format!("slice_{}", self.type_to_c_name(inner)),
CType::Opt(inner) => format!("opt_{}", self.type_to_c_name(inner)),
CType::Res(ok, err) => format!("res_{}_{}", self.type_to_c_name(ok), self.type_to_c_name(err)),
_ => "void".to_string(),
}
}
fn type_base_string(&self, ty: &CType) -> String {
match ty {
CType::Array(inner, _) => self.type_base_string(inner),
_ => self.type_to_string(ty),
}
}
fn type_suffix_string(&self, ty: &CType) -> String {
match ty {
CType::Array(inner, size) => {
format!("[{}]{}", size, self.type_suffix_string(inner))
}
_ => String::new(),
}
}
fn binop_to_string(&self, op: CBinOp) -> &'static str {
match op {
CBinOp::Add => "+",
CBinOp::Sub => "-",
CBinOp::Mul => "*",
CBinOp::Div => "/",
CBinOp::Mod => "%",
CBinOp::Eq => "==",
CBinOp::Ne => "!=",
CBinOp::Lt => "<",
CBinOp::Le => "<=",
CBinOp::Gt => ">",
CBinOp::Ge => ">=",
CBinOp::And => "&&",
CBinOp::Or => "||",
CBinOp::BitAnd => "&",
CBinOp::BitOr => "|",
CBinOp::BitXor => "^",
CBinOp::Shl => "<<",
CBinOp::Shr => ">>",
}
}
fn unaryop_to_string(&self, op: CUnaryOp) -> &'static str {
match op {
CUnaryOp::Neg => "-",
CUnaryOp::Not => "!",
CUnaryOp::BitNot => "~",
}
}
fn line(&mut self, s: &str) {
for _ in 0..self.indent {
self.output.push_str(" ");
}
self.output.push_str(s);
self.output.push('\n');
}
fn blank(&mut self) {
self.output.push('\n');
}
}
impl Default for Emitter {
fn default() -> Self {
Self::new()
}
}