blob: 5e77a48a21b8d41ac0bc7d336770a644c6571007 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::ast::{
self, Attrs, BanjoAst, Constant, Decl, EnumVariant, Ident, Method, StructField, UnionField,
},
crate::backends::util::to_c_name,
crate::backends::Backend,
anyhow::{format_err, Error},
std::io,
std::iter,
};
pub struct CBackend<'a, W: io::Write> {
w: &'a mut W,
}
impl<'a, W: io::Write> CBackend<'a, W> {
pub fn new(w: &'a mut W) -> Self {
CBackend { w }
}
}
pub fn get_doc_comment(attrs: &ast::Attrs, tabs: usize) -> String {
for attr in attrs.0.iter() {
if attr.key == "Doc" {
if let Some(ref val) = attr.val {
let tabs: String = iter::repeat(' ').take(tabs * 4).collect();
return val
.trim_end()
.split("\n")
.map(|line| format!("{}//{}\n", tabs, line))
.collect();
}
}
}
"".to_string()
}
fn ty_to_c_str(ast: &ast::BanjoAst, ty: &ast::Ty) -> Result<String, Error> {
match ty {
ast::Ty::Bool => Ok(String::from("bool")),
ast::Ty::Int8 => Ok(String::from("int8_t")),
ast::Ty::Int16 => Ok(String::from("int16_t")),
ast::Ty::Int32 => Ok(String::from("int32_t")),
ast::Ty::Int64 => Ok(String::from("int64_t")),
ast::Ty::UInt8 => Ok(String::from("uint8_t")),
ast::Ty::UInt16 => Ok(String::from("uint16_t")),
ast::Ty::UInt32 => Ok(String::from("uint32_t")),
ast::Ty::UInt64 => Ok(String::from("uint64_t")),
ast::Ty::USize => Ok(String::from("size_t")),
ast::Ty::Float32 => Ok(String::from("float")),
ast::Ty::Float64 => Ok(String::from("double")),
ast::Ty::Voidptr => Ok(String::from("void")),
ast::Ty::Str { .. } => Ok(String::from("char*")),
ast::Ty::Vector { ref ty, .. } => ty_to_c_str(ast, ty),
ast::Ty::Array { ref ty, .. } => ty_to_c_str(ast, ty),
ast::Ty::Identifier { id, .. } => {
if id.is_base_type() {
Ok(format!("zx_{}_t", id.name()))
} else {
match ast.id_to_type(id) {
ast::Ty::Struct | ast::Ty::Union | ast::Ty::Enum => {
return Ok(format!("{}_t", to_c_name(id.name())));
}
ast::Ty::Protocol => {
if not_callback(ast, id) {
return Ok(format!("{}_protocol_t", to_c_name(id.name())));
} else {
return Ok(format!("{}_t", to_c_name(id.name())));
}
}
t => return ty_to_c_str(ast, &t),
}
}
}
ast::Ty::Handle { .. } => Ok(String::from("zx_handle_t")),
t => Err(format_err!("unknown type in ty_to_c_str {:?}", t)),
}
}
pub fn array_bounds(ast: &ast::BanjoAst, ty: &ast::Ty) -> Option<String> {
if let ast::Ty::Array { ref ty, size, .. } = ty {
return if let Some(bounds) = array_bounds(ast, ty) {
Some(format!("[{}]{}", size.0, bounds))
} else {
Some(format!("[{}]", size.0))
};
}
None
}
fn protocol_to_ops_c_str(ast: &ast::BanjoAst, ty: &ast::Ty) -> Result<String, Error> {
if let ast::Ty::Identifier { id, .. } = ty {
if ast.id_to_type(id) == ast::Ty::Protocol {
return Ok(to_c_name(id.name()) + "_protocol_ops_t");
}
}
Err(format_err!("unknown ident type in protocol_to_ops_c_str {:?}", ty))
}
pub fn not_callback(ast: &ast::BanjoAst, id: &Ident) -> bool {
if let Some(attributes) = ast.id_to_attributes(id) {
if let Some(layout) = attributes.get_attribute("Layout") {
if layout == "ddk-callback" {
return false;
}
}
}
true
}
fn size_to_c_str(ty: &ast::Ty, cons: &ast::Constant, ast: &ast::BanjoAst) -> String {
let Constant(size) = cons;
match ty {
ast::Ty::Int8 => String::from(format!("INT8_C({})", size)),
ast::Ty::Int16 => String::from(format!("INT16_C({})", size)),
ast::Ty::Int32 => String::from(format!("INT32_C({})", size)),
ast::Ty::Int64 => String::from(format!("INT64_C({})", size)),
ast::Ty::UInt8 => String::from(format!("UINT8_C({})", size)),
ast::Ty::UInt16 => String::from(format!("UINT16_C({})", size)),
ast::Ty::UInt32 => String::from(format!("UINT32_C({})", size)),
ast::Ty::UInt64 => String::from(format!("UINT64_C({})", size)),
ast::Ty::USize | ast::Ty::Bool | ast::Ty::Str { .. } => size.clone(),
ast::Ty::Identifier { id, reference: _ } => {
let decl = ast.id_to_decl(id).expect(format!("id: {:?}", id).as_str());
if let Decl::Enum { ty: enum_ty, variants, .. } = decl {
for variant in variants {
if variant.name == *size {
return size_to_c_str(enum_ty, &variant.value, ast);
}
}
}
panic!("don't handle this kind of identifier: {:?}", id);
}
s => panic!("don't handles this sized const: {}", s),
}
}
pub fn name_buffer(ty: &str) -> &'static str {
if ty == "void" {
"buffer"
} else {
"list"
}
}
pub fn name_size(ty: &str) -> &'static str {
if ty == "void" {
"size"
} else {
"count"
}
}
fn struct_attrs_to_c_str(attributes: &Attrs) -> String {
attributes
.0
.iter()
.filter_map(|a| match a.key.as_ref() {
"Packed" => Some("__PACKED"),
_ => None,
})
.collect::<Vec<_>>()
.join(" ")
}
fn field_to_c_str(
attrs: &Attrs,
ty: &ast::Ty,
ident: &Ident,
indent: &str,
ast: &ast::BanjoAst,
) -> Result<String, Error> {
let mut accum = String::new();
accum.push_str(get_doc_comment(attrs, 1).as_str());
let prefix = if ty.is_reference() { "" } else { "const " };
match ty {
ast::Ty::Vector { ty: ref inner_ty, .. } => {
let ty_name = ty_to_c_str(ast, &ty)?;
// TODO(surajmalhotra): Support multi-dimensional vectors.
let ptr = if inner_ty.is_reference() { "*" } else { "" };
accum.push_str(
format!(
"{indent}{prefix}{ty}{ptr}* {c_name}_{buffer};\n\
{indent}size_t {c_name}_{size};",
indent = indent,
buffer = name_buffer(&ty_name),
size = name_size(&ty_name),
c_name = to_c_name(ident.name()),
prefix = prefix,
ty = ty_name,
ptr = ptr,
)
.as_str(),
);
}
ast::Ty::Array { .. } => {
let bounds = array_bounds(ast, &ty).unwrap();
accum.push_str(
format!(
"{indent}{ty} {c_name}{bounds};",
indent = indent,
c_name = to_c_name(ident.name()),
bounds = bounds,
ty = ty_to_c_str(ast, &ty)?
)
.as_str(),
);
}
ast::Ty::Str { ref size, .. } => {
if let Some(size) = size {
accum.push_str(
format!(
"{indent}char {c_name}[{size}];",
indent = indent,
c_name = to_c_name(ident.name()),
size = size,
)
.as_str(),
);
} else {
accum.push_str(
format!(
"{indent}{prefix}{ty} {c_name};",
indent = indent,
c_name = to_c_name(ident.name()),
prefix = prefix,
ty = ty_to_c_str(ast, &ty)?
)
.as_str(),
);
}
}
_ => {
accum.push_str(
format!(
"{indent}{ty} {c_name};",
indent = indent,
c_name = to_c_name(ident.name()),
ty = ty_to_c_str(ast, &ty)?
)
.as_str(),
);
}
}
Ok(accum)
}
fn get_first_param(ast: &BanjoAst, method: &ast::Method) -> Result<(bool, String), Error> {
// Return parameter if a primitive type.
if method.out_params.get(0).map_or(false, |p| p.1.is_primitive(&ast)) {
Ok((true, ty_to_c_str(ast, &method.out_params[0].1)?))
} else {
Ok((false, "void".to_string()))
}
}
fn get_in_params(m: &ast::Method, transform: bool, ast: &BanjoAst) -> Result<Vec<String>, Error> {
m.in_params
.iter()
.map(|(name, ty)| {
match ty {
ast::Ty::Identifier { id, .. } => {
if id.is_base_type() {
let ty_name = ty_to_c_str(ast, ty).unwrap();
return Ok(format!("{} {}", ty_name, to_c_name(name)));
}
match ast.id_to_type(id) {
ast::Ty::Protocol => {
let ty_name = ty_to_c_str(ast, ty).unwrap();
if transform && not_callback(ast, id) {
let ty_name = protocol_to_ops_c_str(ast, ty).unwrap();
Ok(format!(
"void* {name}_ctx, {ty_name}* {name}_ops",
ty_name = ty_name,
name = to_c_name(name)
))
} else {
Ok(format!("const {}* {}", ty_name, to_c_name(name)))
}
}
ast::Ty::Struct | ast::Ty::Union => {
let ty_name = ty_to_c_str(ast, ty).unwrap();
// TODO: Using nullability to determine whether param is mutable is a hack.
let prefix = if ty.is_reference() { "" } else { "const " };
Ok(format!("{}{}* {}", prefix, ty_name, to_c_name(name)))
}
ast::Ty::Enum => {
Ok(format!("{} {}", ty_to_c_str(ast, ty).unwrap(), to_c_name(name)))
}
ast::Ty::Bool
| ast::Ty::Int8
| ast::Ty::Int16
| ast::Ty::Int32
| ast::Ty::Int64
| ast::Ty::UInt8
| ast::Ty::UInt16
| ast::Ty::UInt32
| ast::Ty::UInt64
| ast::Ty::USize
| ast::Ty::Voidptr => {
Ok(format!("{} {}", ty_to_c_str(ast, ty).unwrap(), to_c_name(name)))
}
e => Err(format_err!("unsupported: {}", e)),
}
}
ast::Ty::Str { .. } => {
Ok(format!("const {} {}", ty_to_c_str(ast, ty).unwrap(), to_c_name(name)))
}
ast::Ty::Array { .. } => {
let bounds = array_bounds(ast, ty).unwrap();
let ty = ty_to_c_str(ast, ty).unwrap();
Ok(format!(
"const {ty} {name}{bounds}",
bounds = bounds,
ty = ty,
name = to_c_name(name)
))
}
ast::Ty::Vector { ty: inner_ty, .. } => {
let ty = ty_to_c_str(ast, ty).unwrap();
// TODO(surajmalhotra): Support multi-dimensional vectors.
let ptr = if inner_ty.is_reference() { "*" } else { "" };
Ok(format!(
"const {ty}{ptr}* {name}_{buffer}, size_t {name}_{size}",
buffer = name_buffer(&ty),
size = name_size(&ty),
ty = ty,
ptr = ptr,
name = to_c_name(name)
))
}
_ => Ok(format!("{} {}", ty_to_c_str(ast, ty).unwrap(), to_c_name(name))),
}
})
.collect()
}
fn get_out_params(
m: &ast::Method,
name: &str,
ast: &BanjoAst,
) -> Result<(Vec<String>, String), Error> {
if m.attributes.has_attribute("Async") {
return Ok((
vec![
format!(
"{protocol_name}_{method_name}_callback callback",
protocol_name = to_c_name(name),
method_name = to_c_name(&m.name)
),
"void* cookie".to_string(),
],
"void".to_string(),
));
}
let (skip, return_param) = get_first_param(ast, m)?;
let skip_amt = if skip { 1 } else { 0 };
Ok((m.out_params.iter().skip(skip_amt).map(|(name, ty)| {
let nullable = if ty.is_reference() { "*" } else { "" };
let ty_name = ty_to_c_str(ast, ty).unwrap();
match ty {
ast::Ty::Protocol => format!("const {}* {}", ty_name, to_c_name(name)),
ast::Ty::Array { .. } => {
let bounds = array_bounds(ast, ty).unwrap();
let ty = ty_to_c_str(ast, ty).unwrap();
format!(
"{ty} out_{name}{bounds}",
bounds = bounds,
ty = ty,
name = to_c_name(name)
)
}
ast::Ty::Vector { ty: inner_ty, .. } => {
// TODO(surajmalhotra): Support multi-dimensional vectors.
let ptr = if inner_ty.is_reference() { "*" } else { "" };
if ty.is_reference() {
format!("{ty}{ptr}** out_{name}_{buffer}, size_t* {name}_{size}",
buffer = name_buffer(&ty_name),
size = name_size(&ty_name),
ty = ty_name,
ptr = ptr,
name = to_c_name(name))
} else {
format!("{ty}{ptr}* out_{name}_{buffer}, size_t {name}_{size}, size_t* out_{name}_actual",
buffer = name_buffer(&ty_name),
size = name_size(&ty_name),
ty = ty_name,
ptr = ptr,
name = to_c_name(name))
}
},
ast::Ty::Str {..} => {
format!("{ty} out_{c_name}, size_t {c_name}_capacity",
ty = ty_name, c_name = to_c_name(name))
}
ast::Ty::Handle {..} => format!("{}* out_{}", ty_name, to_c_name(name)),
_ => format!("{}{}* out_{}", ty_name, nullable, to_c_name(name))
}
}).collect(), return_param))
}
fn get_in_args(m: &ast::Method, ast: &BanjoAst) -> Result<Vec<String>, Error> {
Ok(m.in_params
.iter()
.map(|(name, ty)| match ty {
ast::Ty::Vector { .. } => {
let ty = ty_to_c_str(ast, ty).unwrap();
format!(
"{name}_{buffer}, {name}_{size}",
buffer = name_buffer(&ty),
size = name_size(&ty),
name = to_c_name(name)
)
}
_ => format!("{}", to_c_name(name)),
})
.collect())
}
fn get_out_args(m: &ast::Method, ast: &BanjoAst) -> Result<(Vec<String>, bool), Error> {
if m.attributes.has_attribute("Async") {
return Ok((vec!["callback".to_string(), "cookie".to_string()], false));
}
let (skip, _) = get_first_param(ast, m)?;
let skip_amt = if skip { 1 } else { 0 };
Ok((
m.out_params
.iter()
.skip(skip_amt)
.map(|(name, ty)| match ty {
ast::Ty::Protocol { .. } => format!("{}", to_c_name(name)),
ast::Ty::Vector { .. } => {
let ty_name = ty_to_c_str(ast, ty).unwrap();
if ty.is_reference() {
format!(
"out_{name}_{buffer}, {name}_{size}",
buffer = name_buffer(&ty_name),
size = name_size(&ty_name),
name = to_c_name(name)
)
} else {
format!(
"out_{name}_{buffer}, {name}_{size}, out_{name}_actual",
buffer = name_buffer(&ty_name),
size = name_size(&ty_name),
name = to_c_name(name)
)
}
}
ast::Ty::Str { .. } => {
format!("out_{c_name}, {c_name}_capacity", c_name = to_c_name(name))
}
_ => format!("out_{}", to_c_name(name)),
})
.collect(),
skip,
))
}
enum ProtocolType {
Callback,
Interface,
Protocol,
}
impl From<&Attrs> for ProtocolType {
fn from(attributes: &Attrs) -> Self {
if let Some(layout) = attributes.get_attribute("Layout") {
if layout == "ddk-callback" {
ProtocolType::Callback
} else if layout == "ddk-interface" {
ProtocolType::Interface
} else if layout == "ddk-protocol" {
ProtocolType::Protocol
} else {
panic!("Unknown layout attribute: {}", layout);
}
} else {
ProtocolType::Protocol
}
}
}
impl<'a, W: io::Write> CBackend<'a, W> {
fn codegen_enum_decl(
&self,
_attributes: &Attrs,
name: &Ident,
ty: &ast::Ty,
variants: &Vec<EnumVariant>,
ast: &BanjoAst,
) -> Result<String, Error> {
let enum_defines = variants
.iter()
.map(|v| {
Ok(format!(
"#define {c_name}_{v_name} {c_size}",
c_name = to_c_name(name.name()).to_uppercase(),
v_name = v.name.to_uppercase().trim(),
c_size = size_to_c_str(ty, &v.value, ast)
))
})
.collect::<Result<Vec<_>, Error>>()?
.join("\n");
Ok(format!(
"typedef {ty} {c_name}_t;\n{enum_defines}",
c_name = to_c_name(name.name()),
ty = ty_to_c_str(ast, ty)?,
enum_defines = enum_defines
))
}
fn codegen_constant_decl(
&self,
attributes: &Attrs,
name: &Ident,
ty: &ast::Ty,
value: &Constant,
ast: &BanjoAst,
) -> Result<String, Error> {
let mut accum = String::new();
accum.push_str(get_doc_comment(attributes, 0).as_str());
accum.push_str(
format!(
"#define {name} {value}",
name = name.name().trim(),
value = size_to_c_str(ty, value, ast)
)
.as_str(),
);
Ok(accum)
}
fn codegen_union_decl(
&self,
_attributes: &Attrs,
name: &Ident,
_fields: &Vec<UnionField>,
_ast: &BanjoAst,
) -> Result<String, Error> {
Ok(format!("typedef union {c_name} {c_name}_t;", c_name = to_c_name(name.name())))
}
fn codegen_union_def(
&self,
attributes: &Attrs,
name: &Ident,
fields: &Vec<UnionField>,
ast: &BanjoAst,
) -> Result<String, Error> {
let attrs = struct_attrs_to_c_str(attributes);
let members = fields
.iter()
.map(|f| match f.ty {
ast::Ty::Vector { .. } => Err(format_err!("unsupported for UnionField: {:?}", f)),
_ => field_to_c_str(&f.attributes, &f.ty, &f.ident, " ", &ast),
})
.collect::<Result<Vec<_>, Error>>()?
.join("\n");
let mut accum = String::new();
accum.push_str(get_doc_comment(attributes, 0).as_str());
accum.push_str(
format!(
include_str!("templates/c/struct.h"),
c_name = to_c_name(name.name()),
decl = "union",
attrs = if attrs.is_empty() { "".to_string() } else { format!(" {}", attrs) },
members = members
)
.as_str(),
);
Ok(accum)
}
fn codegen_struct_decl(
&self,
_attributes: &Attrs,
name: &Ident,
fields: &Vec<StructField>,
_ast: &BanjoAst,
) -> Result<String, Error> {
// TODO(surajmalhotra): Remove this hack once we no longer include C types.
if fields.len() == 0 {
return Ok("".to_string());
}
Ok(format!("typedef struct {c_name} {c_name}_t;", c_name = to_c_name(name.name())))
}
fn codegen_struct_def(
&self,
attributes: &Attrs,
name: &Ident,
fields: &Vec<StructField>,
ast: &BanjoAst,
) -> Result<String, Error> {
// TODO(surajmalhotra): Remove this hack once we no longer include C types.
if fields.len() == 0 {
return Ok("".to_string());
}
let attrs = struct_attrs_to_c_str(attributes);
let members = fields
.iter()
.map(|f| field_to_c_str(&f.attributes, &f.ty, &f.ident, " ", &ast))
.collect::<Result<Vec<_>, Error>>()?
.join("\n");
let mut accum = String::new();
accum.push_str(get_doc_comment(attributes, 0).as_str());
accum.push_str(
format!(
include_str!("templates/c/struct.h"),
c_name = to_c_name(name.name()),
decl = "struct",
attrs = if attrs.is_empty() { "".to_string() } else { format!(" {}", attrs) },
members = members
)
.as_str(),
);
Ok(accum)
}
fn codegen_protocol_def2(
&self,
name: &str,
methods: &Vec<ast::Method>,
ast: &BanjoAst,
) -> Result<String, Error> {
let fns = methods
.iter()
.map(|m| {
let (out_params, return_param) = get_out_params(&m, name, ast)?;
let in_params = get_in_params(&m, false, ast)?;
let params = iter::once("void* ctx".to_string())
.chain(in_params)
.chain(out_params)
.collect::<Vec<_>>()
.join(", ");
Ok(format!(
" {return_param} (*{fn_name})({params});",
return_param = return_param,
params = params,
fn_name = to_c_name(m.name.as_str())
))
})
.collect::<Result<Vec<_>, Error>>()?
.join("\n");
Ok(format!(include_str!("templates/c/protocol_ops.h"), c_name = to_c_name(name), fns = fns))
}
fn codegen_helper_def(
&self,
name: &str,
methods: &Vec<ast::Method>,
ast: &BanjoAst,
) -> Result<String, Error> {
methods
.iter()
.map(|m| {
let mut accum = String::new();
accum.push_str(get_doc_comment(&m.attributes, 0).as_str());
let (out_params, return_param) = get_out_params(&m, name, ast)?;
let in_params = get_in_params(&m, true, ast)?;
let first_param = format!("const {}_protocol_t* proto", to_c_name(name));
let params = iter::once(first_param)
.chain(in_params)
.chain(out_params)
.collect::<Vec<_>>()
.join(", ");
accum.push_str(
format!(
"static inline {return_param} {protocol_name}_{fn_name}({params}) {{\n",
return_param = return_param,
params = params,
protocol_name = to_c_name(name),
fn_name = to_c_name(m.name.as_str())
)
.as_str(),
);
let (out_args, skip) = get_out_args(&m, ast)?;
let in_args = get_in_args(&m, ast)?;
let proto_args = m
.in_params
.iter()
.filter_map(|(name, ty)| {
if let ast::Ty::Identifier { id, .. } = ty {
if ast.id_to_type(id) == ast::Ty::Protocol && not_callback(ast, id) {
return Some((to_c_name(name), ty_to_c_str(ast, ty).unwrap()));
}
}
None
})
.collect::<Vec<_>>();
for (name, ty) in proto_args.iter() {
accum.push_str(
format!(
include_str!("templates/c/proto_transform.h"),
ty = ty,
name = name
)
.as_str(),
);
}
let args = iter::once("proto->ctx".to_string())
.chain(in_args)
.chain(out_args)
.collect::<Vec<_>>()
.join(", ");
let return_statement = if skip { "return " } else { "" };
accum.push_str(
format!(
" {return_statement}proto->ops->{fn_name}({args});\n",
return_statement = return_statement,
args = args,
fn_name = to_c_name(m.name.as_str())
)
.as_str(),
);
accum.push_str("}\n");
Ok(accum)
})
.collect::<Result<Vec<_>, Error>>()
.map(|x| x.join("\n"))
}
fn codegen_protocol_def(
&self,
attributes: &Attrs,
name: &Ident,
methods: &Vec<Method>,
ast: &BanjoAst,
) -> Result<String, Error> {
Ok(match ProtocolType::from(attributes) {
ProtocolType::Interface | ProtocolType::Protocol => format!(
include_str!("templates/c/protocol.h"),
protocol_name = to_c_name(name.name()),
protocol_def = self.codegen_protocol_def2(name.name(), methods, ast)?,
helper_def = self.codegen_helper_def(name.name(), methods, ast)?
),
ProtocolType::Callback => {
let m = methods.get(0).ok_or(format_err!("callback has no methods"))?;
let (out_params, return_param) = get_out_params(&m, name.name(), ast)?;
let in_params = get_in_params(&m, false, ast)?;
let params = iter::once("void* ctx".to_string())
.chain(in_params)
.chain(out_params)
.collect::<Vec<_>>()
.join(", ");
let method = format!(
"{return_param} (*{fn_name})({params})",
return_param = return_param,
params = params,
fn_name = to_c_name(m.name.as_str())
);
format!(
include_str!("templates/c/callback.h"),
callback_name = to_c_name(name.name()),
callback = method,
)
}
})
}
fn codegen_async_decls(
&self,
name: &Ident,
methods: &Vec<Method>,
ast: &BanjoAst,
) -> Result<String, Error> {
Ok(methods
.iter()
.filter(|method| method.attributes.has_attribute("Async"))
.map(|method| {
let method = ast::Method {
attributes: method.attributes.clone(),
name: method.name.clone(),
in_params: method.out_params.clone(),
out_params: Vec::new(),
};
let in_params = get_in_params(&method, true, ast)?;
let params = iter::once("void* ctx".to_string())
.chain(in_params)
.collect::<Vec<_>>()
.join(", ");
Ok(format!(
"typedef void (*{protocol_name}_{method_name}_callback)({params});\n",
protocol_name = to_c_name(name.name()),
method_name = to_c_name(method.name.as_str()),
params = params
))
})
.collect::<Result<Vec<_>, Error>>()?
.join(""))
}
fn codegen_protocol_decl(
&self,
attributes: &Attrs,
name: &Ident,
methods: &Vec<Method>,
ast: &BanjoAst,
) -> Result<String, Error> {
Ok(match ProtocolType::from(attributes) {
ProtocolType::Interface | ProtocolType::Protocol => format!(
"{async_decls}typedef struct {c_name}_protocol {c_name}_protocol_t;",
async_decls = self.codegen_async_decls(name, methods, ast)?,
c_name = to_c_name(name.name())
),
ProtocolType::Callback => {
format!("typedef struct {c_name} {c_name}_t;", c_name = to_c_name(name.name()))
}
})
}
fn codegen_includes(&self, ast: &BanjoAst) -> Result<String, Error> {
Ok(ast
.namespaces
.iter()
.filter(|n| *n.0 != ast.primary_namespace)
.filter(|n| *n.0 != "zx")
.map(|n| format!("#include <{}.h>", n.0.replace('.', "/")))
.collect::<Vec<_>>()
.join("\n"))
}
}
impl<'a, W: io::Write> Backend<'a, W> for CBackend<'a, W> {
fn codegen(&mut self, ast: BanjoAst) -> Result<(), Error> {
self.w.write_fmt(format_args!(
include_str!("templates/c/header.h"),
includes = self.codegen_includes(&ast)?,
primary_namespace = ast.primary_namespace
))?;
let decl_order = ast.validate_declaration_deps()?;
let declarations = decl_order
.iter()
.filter_map(|decl| match decl {
Decl::Struct { attributes, name, fields } => {
Some(self.codegen_struct_decl(attributes, name, fields, &ast))
}
Decl::Union { attributes, name, fields } => {
Some(self.codegen_union_decl(attributes, name, fields, &ast))
}
Decl::Enum { attributes, name, ty, variants } => {
Some(self.codegen_enum_decl(attributes, name, ty, variants, &ast))
}
Decl::Constant { attributes, name, ty, value } => {
Some(self.codegen_constant_decl(attributes, name, ty, value, &ast))
}
Decl::Protocol { attributes, name, methods } => {
Some(self.codegen_protocol_decl(attributes, name, methods, &ast))
}
Decl::Alias(_to, _from) => None,
Decl::Resource { .. } => None,
})
.collect::<Result<Vec<_>, Error>>()?
.join("\n");
let definitions = decl_order
.iter()
.filter_map(|decl| match decl {
Decl::Struct { attributes, name, fields } => {
Some(self.codegen_struct_def(attributes, name, fields, &ast))
}
Decl::Union { attributes, name, fields } => {
Some(self.codegen_union_def(attributes, name, fields, &ast))
}
Decl::Enum { .. } => None,
Decl::Constant { .. } => None,
Decl::Protocol { attributes, name, methods } => {
Some(self.codegen_protocol_def(attributes, name, methods, &ast))
}
Decl::Alias(_to, _from) => None,
Decl::Resource { .. } => None,
})
.collect::<Result<Vec<_>, Error>>()?
.join("\n");
self.w.write_fmt(format_args!(
include_str!("templates/c/body.h"),
declarations = declarations,
definitions = definitions,
))?;
Ok(())
}
}